diff options
author | marinofaggiana <ios@nextcloud.com> | 2022-05-24 12:06:19 +0300 |
---|---|---|
committer | marinofaggiana <ios@nextcloud.com> | 2022-05-24 12:06:19 +0300 |
commit | e2e52363b045d31542c49c5e12bf517362b69df2 (patch) | |
tree | b3eedb5770b50c5b797031df0f2f751823b134c8 | |
parent | 0611600b1c899f5dafddff6120f4a26e69055299 (diff) | |
parent | 8499b9da1e82c849fb0940f607e319b42845daca (diff) |
Merge branch 'develop'4.4.0
392 files changed, 5748 insertions, 4682 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e52d39172..887141563 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,16 +9,18 @@ on: - master - develop pull_request: + types: [synchronize, opened, reopened, ready_for_review] branches: - master - - develop + - develop jobs: Lint: runs-on: ubuntu-latest - + if: github.event.pull_request.draft == false + steps: - uses: actions/checkout@v2 - + - name: GitHub Action for SwiftLint - uses: norio-nomura/action-swiftlint@3.1.0 + uses: norio-nomura/action-swiftlint@3.1.0
\ No newline at end of file diff --git a/.github/workflows/xcode.yml b/.github/workflows/xcode.yml index 65c3ff88d..02d4facef 100644 --- a/.github/workflows/xcode.yml +++ b/.github/workflows/xcode.yml @@ -1,11 +1,12 @@ name: Build -on: +on: push: - branches: + branches: - master - develop pull_request: + types: [synchronize, opened, reopened, ready_for_review] branches: - master - develop @@ -13,6 +14,7 @@ on: jobs: XCBuild: runs-on: macOS-11 + if: github.event.pull_request.draft == false env: PROJECT: Nextcloud.xcodeproj DESTINATION: platform=iOS Simulator,name=iPhone 11 diff --git a/.swiftlint.yml b/.swiftlint.yml index fd76de49a..ac6e392d4 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -10,10 +10,14 @@ empty_count: severity: warning line_length: - # warning: 120 + # warning: 120, error: 200 warning: 250 error: 250 +function_body_length: + # warning: 40 + warning: 60 + type_body_length: # error: 350 error: 500 @@ -48,7 +52,8 @@ excluded: - iOSClient/Data/NCManageDatabase+Account.swift - iOSClient/Data/NCManageDatabase+Activity.swift - iOSClient/Data/NCManageDatabase.swift - - iOSClient/Data/NCManageDatabse+Metadata.swift + - iOSClient/Data/NCManageDatabase+Metadata.swift + - iOSClient/Data/NCManageDatabase+Video.swift - iOSClient/Diagnostics/NCCapabilitiesViewController.swift - iOSClient/EmptyView/NCEmptyDataSet.swift - iOSClient/Extensions/UIColor+Extensions.swift @@ -105,16 +110,8 @@ excluded: - iOSClient/Select/NCSelect.swift - iOSClient/Settings/NCEndToEndInitialize.swift - iOSClient/Settings/NCManageAutoUploadFileName.swift - - iOSClient/Share/NCShare.swift - - iOSClient/Share/NCShareCommentsCell.swift - iOSClient/Share/NCShareCommon.swift - - iOSClient/Share/NCShareLinkCell.swift - - iOSClient/Share/NCShareLinkMenuView.swift - iOSClient/Share/NCShareNetworking.swift - - iOSClient/Share/NCSharePaging.swift - - iOSClient/Share/NCShareQuickStatusMenu.swift - - iOSClient/Share/NCShareUserCell.swift - - iOSClient/Share/NCShareUserMenuView.swift - iOSClient/Shares/NCShares.swift - iOSClient/Transfers/NCTransferCell.swift - iOSClient/Transfers/NCTransfers.swift @@ -130,13 +127,11 @@ excluded: - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift - iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift - iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift - - iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift - iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift - iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift - iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift - iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift - iOSClient/Viewer/NCViewerProviderContextMenu.swift - - iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift - iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift @@ -1,3 +1,3 @@ -github "https://github.com/marinofaggiana/KTVHTTPCache" "2.0.2" +github "https://github.com/marinofaggiana/KTVHTTPCache" "2.0.5" github "https://github.com/marinofaggiana/TOPasscodeViewController" "master" github "krzyzanowskim/OpenSSL"
\ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index 013ceec7d..ce55f69c1 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ github "krzyzanowskim/OpenSSL" "1.1.1300" -github "marinofaggiana/KTVHTTPCache" "2.0.2" +github "marinofaggiana/KTVHTTPCache" "2.0.5" github "marinofaggiana/TOPasscodeViewController" "a1b9d1058b2648e636525fc368e220a0cfddb42a" diff --git a/File Provider Extension/FileProviderItem.swift b/File Provider Extension/FileProviderItem.swift index ea00cf1a5..28f752099 100644 --- a/File Provider Extension/FileProviderItem.swift +++ b/File Provider Extension/FileProviderItem.swift @@ -60,11 +60,13 @@ class FileProviderItem: NSObject, NSFileProviderItem { } var capabilities: NSFileProviderItemCapabilities { - if metadata.directory { + guard !metadata.directory else { return [ .allowsAddingSubItems, .allowsContentEnumerating, .allowsReading, .allowsDeleting, .allowsRenaming ] - } else { - return [ .allowsWriting, .allowsReading, .allowsDeleting, .allowsRenaming, .allowsReparenting ] } + guard !metadata.lock else { + return [ .allowsReading ] + } + return [ .allowsWriting, .allowsReading, .allowsDeleting, .allowsRenaming, .allowsReparenting ] } var isTrashed: Bool { diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index ad0185bcb..0bd1dd77b 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */; }; 3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */; }; 8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */; }; + AF1A9B6427D0CA1E00F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; }; + AF1A9B6527D0CC0500F17A9E /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */; }; AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E42430AA8000632F5F /* NCCreateFormUploadConflict.swift */; }; AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F704B5E22430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard */; }; AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F704B5E82430C0B800632F5F /* NCCreateFormUploadConflictCell.swift */; }; @@ -29,21 +31,25 @@ AF2D7C7E2742559100ADF566 /* NCShareUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */; }; AF36077127BFA4E8001A243D /* ParallelWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077027BFA4E8001A243D /* ParallelWorker.swift */; }; 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 */; }; 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 */; }; AF4BF617275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; }; - AF4BF61927562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */; }; - AF4BF61A27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */; }; - AF4BF61B27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */; }; - AF4BF61C27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */; }; + AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */; }; + AF4BF61A27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */; }; + AF4BF61B27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */; }; + AF4BF61C27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */; }; AF4BF61E27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; }; AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; }; AF4BF62027562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; }; AF4BF62127562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */; }; + AF56C1DC2784856200D8BAE2 /* NCActivityCommentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF56C1DB2784856200D8BAE2 /* NCActivityCommentView.xib */; }; AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF68326927BE65A90010BF0B /* NCMenuAction.swift */; }; + AF70C14D27F3484D00E13DF2 /* SharePermissionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF70C14C27F3484D00E13DF2 /* SharePermissionTest.swift */; }; + AF730AF827834B1400B7520E /* NCShare+NCCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF730AF727834B1400B7520E /* NCShare+NCCellDelegate.swift */; }; AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */; }; AF7E504E27A2D8FF00B5E4AF /* UIBarButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */; }; AF7E505027A2D92300B5E4AF /* NCSelectableNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */; }; @@ -53,7 +59,20 @@ AF817EF4274BC781009ED85B /* NCUserBaseUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */; }; AF8ED1FC2757821000B8DBC4 /* NextcloudTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */; }; AF8ED2032757822700B8DBC4 /* NCGlobalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF8ED2022757822700B8DBC4 /* NCGlobalTests.swift */; }; + AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471127E2341B002537EE /* NCShare+Menu.swift */; }; + AF93471927E2361E002537EE /* NCShareAdvancePermissionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */; }; + AF93471A27E2361E002537EE /* NCShareAdvancePermissionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */; }; + AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */; }; + AF93471C27E2361E002537EE /* NCShareAdvancePermissionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */; }; + AF93471D27E2361E002537EE /* NCShareAdvancePermissionFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */; }; + AF93474C27E34120002537EE /* NCUtility+Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93474B27E34120002537EE /* NCUtility+Image.swift */; }; + AF93474E27E3F212002537EE /* NCShareNewUserAddComment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */; }; AF935067276B84E700BD078F /* NCMenu+FloatingPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF935066276B84E700BD078F /* NCMenu+FloatingPanel.swift */; }; + AFA2AC8527849604008E1EA7 /* NCActivityCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFA2AC8427849604008E1EA7 /* NCActivityCommentView.swift */; }; + AFCE353327E4ED1900FEA6C2 /* UIToolbar+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353227E4ED1900FEA6C2 /* UIToolbar+Extension.swift */; }; + AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */; }; + AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */; }; + AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */; }; AFD33240276A02C100F5AE02 /* UIApplication+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFD3323F276A02C000F5AE02 /* UIApplication+Orientation.swift */; }; D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */; }; D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */; }; @@ -142,6 +161,7 @@ F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72D404823D2082500A97FD0 /* NCViewerNextcloudText.swift */; }; F72D7EB7263B1207000B3DFC /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = F72D7EB6263B1207000B3DFC /* MarkdownKit */; }; F72DA9B425F53E4E00B87DB1 /* SwiftRichString in Frameworks */ = {isa = PBXBuildFile; productRef = F72DA9B325F53E4E00B87DB1 /* SwiftRichString */; }; + F732D23327CF8AED000B0F1B /* NCPlayerToolBar.xib in Resources */ = {isa = PBXBuildFile; fileRef = F732D23227CF8AED000B0F1B /* NCPlayerToolBar.xib */; }; F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = F733598025C1C188002ABA72 /* NCAskAuthorization.swift */; }; F7362A1F220C853A005101B5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */; }; F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7381EDA218218C9000B1560 /* NCOffline.swift */; }; @@ -188,6 +208,7 @@ F74E7720277A2EF40013B958 /* XLForm in Frameworks */ = {isa = PBXBuildFile; productRef = F74E771F277A2EF40013B958 /* XLForm */; }; F7501C322212E57500FB1415 /* NCMedia.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7501C302212E57400FB1415 /* NCMedia.storyboard */; }; F7501C332212E57500FB1415 /* NCMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7501C312212E57400FB1415 /* NCMedia.swift */; }; + F753BA93281FD8020015BFB6 /* EasyTipView in Frameworks */ = {isa = PBXBuildFile; productRef = F753BA92281FD8020015BFB6 /* EasyTipView */; }; F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F755BD9A20594AC7008C5FBB /* NCService.swift */; }; F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */; }; F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */; }; @@ -219,10 +240,7 @@ F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76673EF22C90433007ED366 /* FileProviderUtility.swift */; }; F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7682FDF23C36B0500983A04 /* NCMainTabBar.swift */; }; F769453C22E9CFFF000A798A /* NCShareUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F769453B22E9CFFF000A798A /* NCShareUserCell.xib */; }; - F769453E22E9E97E000A798A /* NCShareUserMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F769453D22E9E97D000A798A /* NCShareUserMenuView.xib */; }; F769454022E9F077000A798A /* NCSharePaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769453F22E9F077000A798A /* NCSharePaging.swift */; }; - F769454222E9F0EE000A798A /* NCShareLinkMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454122E9F0EE000A798A /* NCShareLinkMenuView.swift */; }; - F769454422E9F142000A798A /* NCShareUserMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454322E9F142000A798A /* NCShareUserMenuView.swift */; }; F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454522E9F1B0000A798A /* NCShareCommon.swift */; }; F769454822E9F20D000A798A /* NCShareNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454722E9F20D000A798A /* NCShareNetworking.swift */; }; F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; }; @@ -239,7 +257,6 @@ F76DA963277B760E0082465B /* Queuer in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA962277B760E0082465B /* Queuer */; }; F76DA966277B76F30082465B /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA965277B76F30082465B /* UICKeyChainStore */; }; F76DA969277B77EA0082465B /* DropDown in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA968277B77EA0082465B /* DropDown */; }; - F76DA96C277B78400082465B /* FSCalendar in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA96B277B78400082465B /* FSCalendar */; }; F76DA96F277B78AE0082465B /* TLPhotoPicker in Frameworks */ = {isa = PBXBuildFile; productRef = F76DA96E277B78AE0082465B /* TLPhotoPicker */; }; F7707687263A853700A1BA94 /* NCContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765608E23BF813500765969 /* NCContentPresenter.swift */; }; F7707689263A896A00A1BA94 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; }; @@ -254,7 +271,7 @@ F771E3F820E239B500AFB62D /* FileProviderExtension+Thumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = F771E3F520E239B400AFB62D /* FileProviderExtension+Thumbnail.swift */; }; F7725A60251F33BB00D125E0 /* NCFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7725A5E251F33BB00D125E0 /* NCFiles.swift */; }; F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7725A5F251F33BB00D125E0 /* NCFiles.storyboard */; }; - F774264A22EB4D0000B23912 /* NCShareUserDropDownCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F774264822EB4D0000B23912 /* NCShareUserDropDownCell.xib */; }; + F774264A22EB4D0000B23912 /* NCSearchUserDropDownCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */; }; F77444F522281649000D5EB0 /* NCGridMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77444F322281649000D5EB0 /* NCGridMediaCell.swift */; }; F77444F622281649000D5EB0 /* NCGridMediaCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F77444F422281649000D5EB0 /* NCGridMediaCell.xib */; }; F77444F8222816D5000D5EB0 /* NCPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77444F7222816D5000D5EB0 /* NCPickerViewController.swift */; }; @@ -269,7 +286,6 @@ 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 */; }; - F77EFC0C26D6751F00806ED6 /* NCShareQuickStatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77EFC0B26D6751F00806ED6 /* NCShareQuickStatusMenu.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 */; }; @@ -299,8 +315,6 @@ F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78F74332163757000C2ADAD /* NCTrash.storyboard */; }; F78F74362163781100C2ADAD /* NCTrash.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78F74352163781100C2ADAD /* NCTrash.swift */; }; F790110E21415BF600D7B136 /* NCViewerRichdocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */; }; - F79728D422F96F2E003CACA7 /* NCShareLinkFolderMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F79728D322F96F2D003CACA7 /* NCShareLinkFolderMenuView.xib */; }; - F79728D622F9A0B1003CACA7 /* NCShareUserFolderMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F79728D522F9A0B0003CACA7 /* NCShareUserFolderMenuView.xib */; }; F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */; }; F798F0E725880609000DAFFD /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */; }; F798F0EC2588060A000DAFFD /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */; }; @@ -356,7 +370,6 @@ F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7CA212B25F1333200826ABB /* NCAccountRequest.swift */; }; F7D96FCC246ED7E200536D73 /* NCNetworkingCheckRemoteUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D96FCB246ED7E100536D73 /* NCNetworkingCheckRemoteUser.swift */; }; F7DBC37C23325E02001A85BA /* NCAppConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DBC37B23325E01001A85BA /* NCAppConfigView.swift */; }; - F7DFAA8A22E22EF100FC4527 /* NCShareLinkMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7DFAA8922E22EF100FC4527 /* NCShareLinkMenuView.xib */; }; F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DFB7EF219C5B8000680748 /* NCCreateFormUploadAssets.swift */; }; F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DFB7F3219C5CA800680748 /* NCCreateFormUploadScanDocument.swift */; }; F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */; }; @@ -364,6 +377,10 @@ F7E572FD278F146C00F8C99E /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F70B86802642CF5400ED5349 /* OpenSSL.xcframework */; }; F7E572FE278F146C00F8C99E /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F70B86802642CF5400ED5349 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; F7E57302278F14FF00F8C99E /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = F70B86802642CF5400ED5349 /* OpenSSL.xcframework */; }; + F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; + F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; + F7E98C1827E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; + F7E98C1927E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; F7EBCDCF277B81FF00A4EF67 /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = F7EBCDCE277B81FF00A4EF67 /* UICKeyChainStore */; }; F7EBCDD1277B820D00A4EF67 /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = F7EBCDD0277B820D00A4EF67 /* UICKeyChainStore */; }; F7EBCDD3277B821700A4EF67 /* UICKeyChainStore in Frameworks */ = {isa = PBXBuildFile; productRef = F7EBCDD2277B821700A4EF67 /* UICKeyChainStore */; }; @@ -384,6 +401,18 @@ F7EFC0C6256BC77700461AAD /* NCMoreUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EFC0C5256BC77700461AAD /* NCMoreUserCell.xib */; }; F7EFC0CD256BF8DD00461AAD /* NCUserStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */; }; F7F1E54C2492369A00E42386 /* NCMediaCommandView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7F1E54B2492369A00E42386 /* NCMediaCommandView.xib */; }; + F7F4F0F727ECDBA4008676F9 /* NCSubtitles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F0F327ECDBA4008676F9 /* NCSubtitles.swift */; }; + F7F4F0F927ECDBA4008676F9 /* NCSubtitlePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F0F527ECDBA4008676F9 /* NCSubtitlePlayer.swift */; }; + F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */; }; + F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */; }; + F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */; }; + F7F4F10827ECDBDB008676F9 /* Inconsolata-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10027ECDBDB008676F9 /* Inconsolata-ExtraLight.ttf */; }; + F7F4F10927ECDBDB008676F9 /* Inconsolata-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10127ECDBDB008676F9 /* Inconsolata-Bold.ttf */; }; + F7F4F10A27ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10227ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf */; }; + F7F4F10B27ECDBDB008676F9 /* Inconsolata-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10327ECDBDB008676F9 /* Inconsolata-Light.ttf */; }; + F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F10427ECDBDB008676F9 /* Inconsolata-Regular.ttf */; }; + F7F4F11027ECDC4A008676F9 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F10F27ECDC4A008676F9 /* UIDevice+Extensions.swift */; }; + F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */; }; 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 */; }; @@ -465,6 +494,7 @@ 371B5A3223D0BD5500FAFAE9 /* FloatingPanel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FloatingPanel.framework; path = Carthage/Build/iOS/FloatingPanel.framework; sourceTree = "<group>"; }; 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = "<group>"; }; 8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Menu.swift"; sourceTree = "<group>"; }; + AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Extension.swift"; sourceTree = "<group>"; }; AF22B20B277C6F4D00DAB0CC /* NCShareCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCell.swift; sourceTree = "<group>"; }; AF22B215277D196700DAB0CC /* NCShareExtension+DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+DataSource.swift"; sourceTree = "<group>"; }; AF22B216277D196700DAB0CC /* NCShareExtension+Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+Files.swift"; sourceTree = "<group>"; }; @@ -472,11 +502,15 @@ AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareUserCell.swift; sourceTree = "<group>"; }; AF36077027BFA4E8001A243D /* ParallelWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorker.swift; sourceTree = "<group>"; }; AF36077527BFB019001A243D /* ParallelWorkerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallelWorkerTest.swift; sourceTree = "<group>"; }; + AF3F909928213BEA0048A93E /* UserAgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentTests.swift; sourceTree = "<group>"; }; AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCTrash+CollectionView.swift"; sourceTree = "<group>"; }; AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Account.swift"; sourceTree = "<group>"; }; - AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabse+Metadata.swift"; sourceTree = "<group>"; }; + AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Metadata.swift"; sourceTree = "<group>"; }; AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Activity.swift"; sourceTree = "<group>"; }; + AF56C1DB2784856200D8BAE2 /* NCActivityCommentView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCActivityCommentView.xib; sourceTree = "<group>"; }; AF68326927BE65A90010BF0B /* NCMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMenuAction.swift; sourceTree = "<group>"; }; + AF70C14C27F3484D00E13DF2 /* SharePermissionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePermissionTest.swift; sourceTree = "<group>"; }; + AF730AF727834B1400B7520E /* NCShare+NCCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+NCCellDelegate.swift"; sourceTree = "<group>"; }; AF730AF927843E4C00B7520E /* NCShareExtension+NCDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShareExtension+NCDelegate.swift"; sourceTree = "<group>"; }; AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBarButton+Extension.swift"; sourceTree = "<group>"; }; AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSelectableNavigationView.swift; sourceTree = "<group>"; }; @@ -484,7 +518,20 @@ AF8ED1F92757821000B8DBC4 /* NextcloudTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NextcloudTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudTests.swift; sourceTree = "<group>"; }; AF8ED2022757822700B8DBC4 /* NCGlobalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCGlobalTests.swift; sourceTree = "<group>"; }; + AF93471127E2341B002537EE /* NCShare+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Menu.swift"; sourceTree = "<group>"; }; + AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareAdvancePermissionFooter.xib; sourceTree = "<group>"; }; + AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermissionHeader.swift; sourceTree = "<group>"; }; + AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermission.swift; sourceTree = "<group>"; }; + AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareAdvancePermissionHeader.xib; sourceTree = "<group>"; }; + AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareAdvancePermissionFooter.swift; sourceTree = "<group>"; }; + AF93474B27E34120002537EE /* NCUtility+Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Image.swift"; sourceTree = "<group>"; }; + AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareNewUserAddComment.swift; sourceTree = "<group>"; }; AF935066276B84E700BD078F /* NCMenu+FloatingPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCMenu+FloatingPanel.swift"; sourceTree = "<group>"; }; + AFA2AC8427849604008E1EA7 /* NCActivityCommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityCommentView.swift; sourceTree = "<group>"; }; + AFCE353227E4ED1900FEA6C2 /* UIToolbar+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIToolbar+Extension.swift"; sourceTree = "<group>"; }; + AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Extension.swift"; sourceTree = "<group>"; }; + AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCells.swift; sourceTree = "<group>"; }; + AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCShare+Helper.swift"; sourceTree = "<group>"; }; AFD3323F276A02C000F5AE02 /* UIApplication+Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+Orientation.swift"; sourceTree = "<group>"; }; D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = "<group>"; }; F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = "<group>"; }; @@ -610,6 +657,7 @@ F72E0B9C21AD60BC00898D7B /* WeScan.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WeScan.framework; path = Carthage/Build/iOS/WeScan.framework; sourceTree = "<group>"; }; F7320934201B812F008A0888 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = "<group>"; }; F732093B201B81E4008A0888 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; }; + F732D23227CF8AED000B0F1B /* NCPlayerToolBar.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCPlayerToolBar.xib; sourceTree = "<group>"; }; F733598025C1C188002ABA72 /* NCAskAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCAskAuthorization.swift; sourceTree = "<group>"; }; F733B65121997CC1001C1FFA /* TLPhotoPicker.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TLPhotoPicker.framework; path = Carthage/Build/iOS/TLPhotoPicker.framework; sourceTree = "<group>"; }; F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; }; @@ -682,10 +730,7 @@ F76673EF22C90433007ED366 /* FileProviderUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileProviderUtility.swift; sourceTree = "<group>"; }; F7682FDF23C36B0500983A04 /* NCMainTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMainTabBar.swift; sourceTree = "<group>"; }; F769453B22E9CFFF000A798A /* NCShareUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareUserCell.xib; sourceTree = "<group>"; }; - F769453D22E9E97D000A798A /* NCShareUserMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareUserMenuView.xib; sourceTree = "<group>"; }; F769453F22E9F077000A798A /* NCSharePaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSharePaging.swift; sourceTree = "<group>"; }; - F769454122E9F0EE000A798A /* NCShareLinkMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareLinkMenuView.swift; sourceTree = "<group>"; }; - F769454322E9F142000A798A /* NCShareUserMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareUserMenuView.swift; sourceTree = "<group>"; }; 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>"; }; @@ -706,7 +751,7 @@ F7725A5E251F33BB00D125E0 /* NCFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCFiles.swift; sourceTree = "<group>"; }; F7725A5F251F33BB00D125E0 /* NCFiles.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCFiles.storyboard; sourceTree = "<group>"; }; F774264022EB3F7300B23912 /* DropDown.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DropDown.framework; path = Carthage/Build/iOS/DropDown.framework; sourceTree = "<group>"; }; - F774264822EB4D0000B23912 /* NCShareUserDropDownCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareUserDropDownCell.xib; sourceTree = "<group>"; }; + F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSearchUserDropDownCell.xib; sourceTree = "<group>"; }; F77438EB1FCD694900662C46 /* ka-GE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ka-GE"; path = "ka-GE.lproj/Localizable.strings"; sourceTree = "<group>"; }; F77438F21FCD69D300662C46 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; }; F77438F91FCD6A0D00662C46 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; }; @@ -731,7 +776,6 @@ 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>"; }; - F77EFC0B26D6751F00806ED6 /* NCShareQuickStatusMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCShareQuickStatusMenu.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>"; }; @@ -755,8 +799,6 @@ F78F74352163781100C2ADAD /* NCTrash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrash.swift; sourceTree = "<group>"; }; F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerRichdocument.swift; sourceTree = "<group>"; }; F79018A424092EF4007C9B6D /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; }; - F79728D322F96F2D003CACA7 /* NCShareLinkFolderMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareLinkFolderMenuView.xib; sourceTree = "<group>"; }; - F79728D522F9A0B0003CACA7 /* NCShareUserFolderMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareUserFolderMenuView.xib; sourceTree = "<group>"; }; F79918A021997F9000C2E308 /* UICKeyChainStore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UICKeyChainStore.framework; path = Carthage/Build/iOS/UICKeyChainStore.framework; sourceTree = "<group>"; }; F79918A72199840500C2E308 /* Sheeeeeeeeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sheeeeeeeeet.framework; path = Carthage/Build/iOS/Sheeeeeeeeet.framework; sourceTree = "<group>"; }; F79A65C22191D90F00FF6DCC /* NCSelect.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCSelect.storyboard; sourceTree = "<group>"; }; @@ -875,13 +917,13 @@ F7DBC37B23325E01001A85BA /* NCAppConfigView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCAppConfigView.swift; sourceTree = "<group>"; }; F7DBD82B23E46A4700ECB7C6 /* MarkdownKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MarkdownKit.framework; path = Carthage/Build/iOS/MarkdownKit.framework; sourceTree = "<group>"; }; F7DE9AB01F482FA5008DFE10 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; }; - F7DFAA8922E22EF100FC4527 /* NCShareLinkMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareLinkMenuView.xib; sourceTree = "<group>"; }; F7DFB7EF219C5B8000680748 /* NCCreateFormUploadAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadAssets.swift; sourceTree = "<group>"; }; F7DFB7F3219C5CA800680748 /* NCCreateFormUploadScanDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadScanDocument.swift; sourceTree = "<group>"; }; F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCUserStatus.storyboard; sourceTree = "<group>"; }; F7E45E6D21E75BF200579249 /* ja-JP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja-JP"; path = "ja-JP.lproj/Localizable.strings"; sourceTree = "<group>"; }; F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCommentsCell.swift; sourceTree = "<group>"; }; F7E856182351D7BE009A3330 /* SwiftyXMLParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyXMLParser.framework; path = Carthage/Build/iOS/SwiftyXMLParser.framework; sourceTree = "<group>"; }; + F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Video.swift"; sourceTree = "<group>"; }; F7EDE508262DA9D600414FE6 /* NCSelectCommandViewSelect.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSelectCommandViewSelect.xib; sourceTree = "<group>"; }; F7EDE513262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "NCSelectCommandViewSelect+CreateFolder.xib"; sourceTree = "<group>"; }; F7EDE51A262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSelectCommandViewCopyMove.xib; sourceTree = "<group>"; }; @@ -890,6 +932,18 @@ F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUserStatus.swift; sourceTree = "<group>"; }; F7F1E54B2492369A00E42386 /* NCMediaCommandView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMediaCommandView.xib; sourceTree = "<group>"; }; F7F35B592578FB63003F5589 /* CollaboraOnlineWebViewKeyboardManager.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CollaboraOnlineWebViewKeyboardManager.framework; path = Carthage/Build/iOS/CollaboraOnlineWebViewKeyboardManager.framework; sourceTree = "<group>"; }; + F7F4F0F327ECDBA4008676F9 /* NCSubtitles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSubtitles.swift; sourceTree = "<group>"; }; + F7F4F0F527ECDBA4008676F9 /* NCSubtitlePlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSubtitlePlayer.swift; sourceTree = "<group>"; }; + F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-SemiBold.ttf"; sourceTree = "<group>"; }; + F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Medium.ttf"; sourceTree = "<group>"; }; + F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Black.ttf"; sourceTree = "<group>"; }; + F7F4F10027ECDBDB008676F9 /* Inconsolata-ExtraLight.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-ExtraLight.ttf"; sourceTree = "<group>"; }; + F7F4F10127ECDBDB008676F9 /* Inconsolata-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Bold.ttf"; sourceTree = "<group>"; }; + F7F4F10227ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-ExtraBold.ttf"; sourceTree = "<group>"; }; + F7F4F10327ECDBDB008676F9 /* Inconsolata-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Light.ttf"; sourceTree = "<group>"; }; + F7F4F10427ECDBDB008676F9 /* Inconsolata-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Regular.ttf"; sourceTree = "<group>"; }; + F7F4F10F27ECDC4A008676F9 /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = "<group>"; }; + F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+Extension.swift"; sourceTree = "<group>"; }; F7F67BB81A24D27800EE80DA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; 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>"; }; @@ -964,6 +1018,7 @@ F758A01227A7F03E0069468B /* JGProgressHUD in Frameworks */, F76DA96F277B78AE0082465B /* TLPhotoPicker in Frameworks */, F76DA966277B76F30082465B /* UICKeyChainStore in Frameworks */, + F753BA93281FD8020015BFB6 /* EasyTipView in Frameworks */, F76DA95B277B75A90082465B /* TOPasscodeViewController.xcframework in Frameworks */, F76DA963277B760E0082465B /* Queuer in Frameworks */, F75E57BD25BF0EC1002B72C2 /* SVGKit in Frameworks */, @@ -974,7 +1029,6 @@ F72DA9B425F53E4E00B87DB1 /* SwiftRichString in Frameworks */, F74E7720277A2EF40013B958 /* XLForm in Frameworks */, F73ADD1C265546890069EA0D /* SwiftEntryKit in Frameworks */, - F76DA96C277B78400082465B /* FSCalendar in Frameworks */, F76DA93F277B75870082465B /* KTVCocoaHTTPServer.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1005,6 +1059,7 @@ F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */, F7CBC31B24F78E79004D3812 /* NCSortMenu.swift */, F75D19E225EFE09000D74598 /* NCTrash+Menu.swift */, + AF93471127E2341B002537EE /* NCShare+Menu.swift */, F710D2012405826100A6033D /* NCViewer+Menu.swift */, ); path = Menu; @@ -1015,11 +1070,27 @@ children = ( AF8ED2022757822700B8DBC4 /* NCGlobalTests.swift */, AF36077527BFB019001A243D /* ParallelWorkerTest.swift */, + AF70C14C27F3484D00E13DF2 /* SharePermissionTest.swift */, + AF3F909928213BEA0048A93E /* UserAgentTests.swift */, AF8ED1FB2757821000B8DBC4 /* NextcloudTests.swift */, ); path = NextcloudTests; sourceTree = "<group>"; }; + AF93471327E235EB002537EE /* Advanced */ = { + isa = PBXGroup; + children = ( + AF93471627E2361E002537EE /* NCShareAdvancePermission.swift */, + AFCE353627E4ED7B00FEA6C2 /* NCShareCells.swift */, + AF93471827E2361E002537EE /* NCShareAdvancePermissionFooter.swift */, + AF93471427E2361E002537EE /* NCShareAdvancePermissionFooter.xib */, + AF93474D27E3F211002537EE /* NCShareNewUserAddComment.swift */, + AF93471527E2361E002537EE /* NCShareAdvancePermissionHeader.swift */, + AF93471727E2361E002537EE /* NCShareAdvancePermissionHeader.xib */, + ); + path = Advanced; + sourceTree = "<group>"; + }; F70211F31BAC56E9003FC03E /* Main */ = { isa = PBXGroup; children = ( @@ -1153,24 +1224,20 @@ children = ( F700510022DF63AC003A3356 /* NCShare.storyboard */, F700510422DF6A89003A3356 /* NCShare.swift */, + AFCE353827E5DE0400FEA6C2 /* NCShare+Helper.swift */, + AF730AF727834B1400B7520E /* NCShare+NCCellDelegate.swift */, + AF93471327E235EB002537EE /* Advanced */, F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */, F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */, F769454522E9F1B0000A798A /* NCShareCommon.swift */, F73CB3B122E072A000AD728E /* NCShareHeaderView.xib */, F787704E22E7019900F287A9 /* NCShareLinkCell.xib */, AF2D7C7B2742556F00ADF566 /* NCShareLinkCell.swift */, - F79728D322F96F2D003CACA7 /* NCShareLinkFolderMenuView.xib */, - F769454122E9F0EE000A798A /* NCShareLinkMenuView.swift */, - F7DFAA8922E22EF100FC4527 /* NCShareLinkMenuView.xib */, F769454722E9F20D000A798A /* NCShareNetworking.swift */, F769453F22E9F077000A798A /* NCSharePaging.swift */, - F77EFC0B26D6751F00806ED6 /* NCShareQuickStatusMenu.swift */, + F774264822EB4D0000B23912 /* NCSearchUserDropDownCell.xib */, F769453B22E9CFFF000A798A /* NCShareUserCell.xib */, AF2D7C7D2742559100ADF566 /* NCShareUserCell.swift */, - F774264822EB4D0000B23912 /* NCShareUserDropDownCell.xib */, - F79728D522F9A0B0003CACA7 /* NCShareUserFolderMenuView.xib */, - F769453D22E9E97D000A798A /* NCShareUserMenuView.xib */, - F769454322E9F142000A798A /* NCShareUserMenuView.swift */, ); path = Share; sourceTree = "<group>"; @@ -1377,7 +1444,9 @@ F79EDA9E26B004980007D134 /* NCPlayer */ = { isa = PBXGroup; children = ( + F7F4F0F227ECDBA4008676F9 /* NCSubtitle */, F79EDAA126B004980007D134 /* NCPlayer.swift */, + F732D23227CF8AED000B0F1B /* NCPlayerToolBar.xib */, F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */, F716B75E26F09DF600D37EFC /* NCKTVHTTPCache.swift */, ); @@ -1392,11 +1461,16 @@ F78071071EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.h */, F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */, F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */, + AFD3323F276A02C000F5AE02 /* UIApplication+Orientation.swift */, + AFCE353227E4ED1900FEA6C2 /* UIToolbar+Extension.swift */, + AF1A9B6327D0CA1E00F17A9E /* UIAlertController+Extension.swift */, + AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */, + AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */, F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */, F79B645F26CA661600838ACA /* UIControl+Extensions.swift */, - AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */, + F7F4F10F27ECDC4A008676F9 /* UIDevice+Extensions.swift */, + F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */, F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */, - AFD3323F276A02C000F5AE02 /* UIApplication+Orientation.swift */, F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */, F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */, ); @@ -1416,7 +1490,9 @@ isa = PBXGroup; children = ( F7C9555221F0C4CA0024296E /* NCActivity.storyboard */, + AF56C1DB2784856200D8BAE2 /* NCActivityCommentView.xib */, F7C9555421F0C5470024296E /* NCActivity.swift */, + AFA2AC8427849604008E1EA7 /* NCActivityCommentView.swift */, D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */, ); path = Activity; @@ -1473,7 +1549,8 @@ F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */, AF4BF61D27562B3F0081CEEF /* NCManageDatabase+Activity.swift */, AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */, - AF4BF61827562A4B0081CEEF /* NCManageDatabse+Metadata.swift */, + AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */, + F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */, F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */, ); path = Data; @@ -1503,6 +1580,7 @@ F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */, F707C26421A2DC5200F6181E /* NCStoreReview.swift */, F70BFC7320E0FA7C00C67599 /* NCUtility.swift */, + AF93474B27E34120002537EE /* NCUtility+Image.swift */, AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */, F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */, AF36077027BFA4E8001A243D /* ParallelWorker.swift */, @@ -1642,6 +1720,38 @@ path = UserStatus; sourceTree = "<group>"; }; + F7F4F0F227ECDBA4008676F9 /* NCSubtitle */ = { + isa = PBXGroup; + children = ( + F7F4F0F327ECDBA4008676F9 /* NCSubtitles.swift */, + F7F4F0F527ECDBA4008676F9 /* NCSubtitlePlayer.swift */, + ); + path = NCSubtitle; + sourceTree = "<group>"; + }; + F7F4F0FB27ECDBDA008676F9 /* Font */ = { + isa = PBXGroup; + children = ( + F7F4F0FC27ECDBDB008676F9 /* Inconsolata */, + ); + path = Font; + sourceTree = "<group>"; + }; + F7F4F0FC27ECDBDB008676F9 /* Inconsolata */ = { + isa = PBXGroup; + children = ( + F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */, + F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */, + F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */, + F7F4F10027ECDBDB008676F9 /* Inconsolata-ExtraLight.ttf */, + F7F4F10127ECDBDB008676F9 /* Inconsolata-Bold.ttf */, + F7F4F10227ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf */, + F7F4F10327ECDBDB008676F9 /* Inconsolata-Light.ttf */, + F7F4F10427ECDBDB008676F9 /* Inconsolata-Regular.ttf */, + ); + path = Inconsolata; + sourceTree = "<group>"; + }; F7F67B9F1A24D27800EE80DA = { isa = PBXGroup; children = ( @@ -1673,6 +1783,7 @@ F7BAAD951ED5A63D00B7EAD4 /* Data */, F73FAEE224D2CA830090692E /* Diagnostics */, F723986F253D867900257F49 /* EmptyView */, + F7A0D14E259229FA008F8A13 /* Extensions */, F7A3214D1E9E2A070069AD1B /* Favorites */, F7725A5D251F33BB00D125E0 /* Files */, F7A80BC7252624C100C7CD01 /* FileViewInFolder */, @@ -1696,7 +1807,6 @@ F7E9C41320F4CA870040CF18 /* Transfers */, F78F74322163753B00C2ADAD /* Trash */, F7EFC0CB256BF89300461AAD /* UserStatus */, - F7A0D14E259229FA008F8A13 /* Extensions */, F7BFFA991A24D7BB0044ED85 /* Utility */, F79630EC215526B60015EEA5 /* Viewer */, ); @@ -1706,6 +1816,7 @@ F7F67BAB1A24D27800EE80DA /* Supporting Files */ = { isa = PBXGroup; children = ( + F7F4F0FB27ECDBDA008676F9 /* Font */, F72B60941A24F04E004EF66F /* Localizations */, F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */, ); @@ -1970,13 +2081,13 @@ F76DA962277B760E0082465B /* Queuer */, F76DA965277B76F30082465B /* UICKeyChainStore */, F76DA968277B77EA0082465B /* DropDown */, - F76DA96B277B78400082465B /* FSCalendar */, F76DA96E277B78AE0082465B /* TLPhotoPicker */, F710FC79277B7D0000AA9FBF /* Realm */, F710FC7B277B7D0000AA9FBF /* RealmSwift */, F7233B3927835FA400F40A43 /* ChromaColorPicker */, F7BB7E4627A18C56009B9F29 /* Parchment */, F758A01127A7F03E0069468B /* JGProgressHUD */, + F753BA92281FD8020015BFB6 /* EasyTipView */, ); productName = "Crypto Cloud"; productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */; @@ -1989,7 +2100,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1310; - LastUpgradeCheck = 1320; + LastUpgradeCheck = 1330; ORGANIZATIONNAME = "Marino Faggiana"; TargetAttributes = { 2C33C47E23E2C475005F963B = { @@ -2098,12 +2209,12 @@ F76DA961277B760E0082465B /* XCRemoteSwiftPackageReference "Queuer" */, F76DA964277B76F10082465B /* XCRemoteSwiftPackageReference "UICKeyChainStore" */, F76DA967277B77E90082465B /* XCRemoteSwiftPackageReference "DropDown" */, - F76DA96A277B78400082465B /* XCRemoteSwiftPackageReference "FSCalendar" */, F76DA96D277B78AE0082465B /* XCRemoteSwiftPackageReference "TLPhotoPicker" */, F710FC78277B7CFF00AA9FBF /* XCRemoteSwiftPackageReference "realm-swift" */, F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */, F7BB7E4527A18C56009B9F29 /* XCRemoteSwiftPackageReference "Parchment" */, F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */, + F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */, ); productRefGroup = F7F67B9F1A24D27800EE80DA; projectDirPath = ""; @@ -2167,13 +2278,15 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F79728D422F96F2E003CACA7 /* NCShareLinkFolderMenuView.xib in Resources */, F7362A1F220C853A005101B5 /* LaunchScreen.storyboard in Resources */, F77444F622281649000D5EB0 /* NCGridMediaCell.xib in Resources */, F78ACD4421903CF20088454D /* NCListCell.xib in Resources */, + F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */, F78ACD4621903D010088454D /* NCGridCell.xib in Resources */, + F7F4F10827ECDBDB008676F9 /* Inconsolata-ExtraLight.ttf in Resources */, 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 */, @@ -2188,48 +2301,54 @@ F7226EDC1EE4089300EBECB1 /* Main.storyboard in Resources */, 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 */, F7D0F33E264144FC0097D4A3 /* Background.xcassets in Resources */, F7F1E54C2492369A00E42386 /* NCMediaCommandView.xib in Resources */, F710E8111EF95C9C00DC2427 /* ImagesIntro.xcassets in Resources */, F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */, + F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */, F74C0437253F1CDC009762AB /* NCShares.storyboard in Resources */, + F7F4F10C27ECDBDB008676F9 /* Inconsolata-Regular.ttf in Resources */, F7B8B83025681C3400967775 /* GoogleService-Info.plist in Resources */, F7381EE5218218C9000B1560 /* NCOffline.storyboard in Resources */, F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */, F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */, - F769453E22E9E97E000A798A /* NCShareUserMenuView.xib in Resources */, F7CA212E25F1333300826ABB /* NCAccountRequest.storyboard in Resources */, F749C10E23C4A5340027D966 /* NCIntroCollectionViewCell.xib in Resources */, F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */, F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */, F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */, + AF93471927E2361E002537EE /* NCShareAdvancePermissionFooter.xib in Resources */, F7725A61251F33BB00D125E0 /* NCFiles.storyboard in Resources */, F700510122DF63AC003A3356 /* NCShare.storyboard in Resources */, F787704F22E7019900F287A9 /* NCShareLinkCell.xib in Resources */, F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */, F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */, + F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */, F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */, F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */, F747BA1F22354D2000971601 /* NCCreateFormUploadVoiceNote.storyboard in Resources */, F7651A8A23A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard in Resources */, + F7F4F10A27ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf in Resources */, F704B5E72430C06700632F5F /* NCCreateFormUploadConflictCell.xib in Resources */, - F79728D622F9A0B1003CACA7 /* NCShareUserFolderMenuView.xib in Resources */, - F7DFAA8A22E22EF100FC4527 /* NCShareLinkMenuView.xib in Resources */, F7C9555321F0C4CA0024296E /* NCActivity.storyboard in Resources */, F7BC287E26663F6C004D46C5 /* NCViewCertificateDetails.storyboard in Resources */, F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */, F704B5E32430AA6F00632F5F /* NCCreateFormUploadConflict.storyboard in Resources */, F77B0F611D118A16002130FE /* Acknowledgements.rtf in Resources */, F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */, + F732D23327CF8AED000B0F1B /* NCPlayerToolBar.xib in Resources */, F73D11FA253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard in Resources */, F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */, F73B422B2476764F00A30FD3 /* NCNotification.storyboard in Resources */, F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */, F77B0F631D118A16002130FE /* Localizable.strings in Resources */, F7632FC1218353AA00721B71 /* NCTrashSectionFooter.xib in Resources */, - F774264A22EB4D0000B23912 /* NCShareUserDropDownCell.xib in Resources */, + F774264A22EB4D0000B23912 /* NCSearchUserDropDownCell.xib in Resources */, F7CB689A2541676B0050EC94 /* NCMore.storyboard in Resources */, F70B866D2642A21300ED5349 /* NCBackgroundImageColor.storyboard in Resources */, F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */, @@ -2297,13 +2416,14 @@ F702F2D225EE5B5C008F8E80 /* NCGlobal.swift in Sources */, F7707689263A896A00A1BA94 /* UIImage+Extensions.swift in Sources */, 2C1D5D7523E2DE3300334ABB /* NCDatabase.swift in Sources */, + F7E98C1927E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, 2C1D5D7623E2DE3300334ABB /* NCManageDatabase.swift in Sources */, 2C33C48223E2C475005F963B /* NotificationService.swift in Sources */, AF4BF617275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */, D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */, F73D5E4A246DE09200DF6467 /* NCElementsJSON.swift in Sources */, F79B646326CA661600838ACA /* UIControl+Extensions.swift in Sources */, - AF4BF61C27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */, + AF4BF61C27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, AF817EF4274BC781009ED85B /* NCUserBaseUrl.swift in Sources */, F798F0EC2588060A000DAFFD /* UIColor+Extensions.swift in Sources */, 2CB7D1CA23E2EDCB00376EF9 /* NCPushNotificationEncryption.m in Sources */, @@ -2315,6 +2435,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AF70C14D27F3484D00E13DF2 /* SharePermissionTest.swift in Sources */, + AF3F909A28213BEA0048A93E /* UserAgentTests.swift in Sources */, AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */, AF8ED1FC2757821000B8DBC4 /* NextcloudTests.swift in Sources */, AF8ED2032757822700B8DBC4 /* NCGlobalTests.swift in Sources */, @@ -2336,15 +2458,17 @@ F70460532499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */, F70BFC7520E0FA7D00C67599 /* NCUtility.swift in Sources */, AF22B20C277C6F4D00DAB0CC /* NCShareCell.swift in Sources */, + F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, F79B646126CA661600838ACA /* UIControl+Extensions.swift in Sources */, F7EDE4CC262D7B6F00414FE6 /* NCEmptyDataSet.swift in Sources */, - AF4BF61A27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */, + AF4BF61A27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */, F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */, AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */, AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */, F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */, F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */, + AF1A9B6527D0CC0500F17A9E /* UIAlertController+Extension.swift in Sources */, AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */, F7BD71E62636EAFC00643C34 /* NCNetworkingE2EE.swift in Sources */, F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */, @@ -2387,8 +2511,9 @@ AF4BF616275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */, F7434B3620E23FE000417916 /* NCManageDatabase.swift in Sources */, F798F0E725880609000DAFFD /* UIColor+Extensions.swift in Sources */, - AF4BF61B27562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */, + AF4BF61B27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, F70460542499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */, + F7E98C1827E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, F785EEA42461A4A600B3F945 /* NCUtility.swift in Sources */, F79B646226CA661600838ACA /* UIControl+Extensions.swift in Sources */, AF817EF3274BC781009ED85B /* NCUserBaseUrl.swift in Sources */, @@ -2417,6 +2542,7 @@ 370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */, F77B0DF51D118A16002130FE /* CCUtility.m in Sources */, 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 */, @@ -2429,7 +2555,9 @@ F73F537F1E929C8500F8678D /* NCMore.swift in Sources */, F702F2CF25EE5B5C008F8E80 /* NCGlobal.swift in Sources */, F72CD63A25C19EBF00F46F9A /* NCAutoUpload.swift in Sources */, + AF93471D27E2361E002537EE /* NCShareAdvancePermissionFooter.swift in Sources */, F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */, + AF1A9B6427D0CA1E00F17A9E /* UIAlertController+Extension.swift in Sources */, F73B422C2476764F00A30FD3 /* NCNotification.swift in Sources */, 371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */, F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */, @@ -2441,9 +2569,11 @@ F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */, F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */, F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */, - AF4BF61927562A4B0081CEEF /* NCManageDatabse+Metadata.swift in Sources */, + AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */, + AFA2AC8527849604008E1EA7 /* NCActivityCommentView.swift in Sources */, F716B75F26F09DF600D37EFC /* NCKTVHTTPCache.swift in Sources */, + AFCE353727E4ED7B00FEA6C2 /* NCShareCells.swift in Sources */, AF36077127BFA4E8001A243D /* ParallelWorker.swift in Sources */, F75A9EE623796C6F0044CFCE /* NCNetworking.swift in Sources */, F758B460212C56A400515F55 /* NCScan.swift in Sources */, @@ -2455,9 +2585,9 @@ F72928A0253B0937009CA4FD /* NCMainNavigationController.swift in Sources */, F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */, F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */, + AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */, F700510522DF6A89003A3356 /* NCShare.swift in Sources */, F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */, - F769454222E9F0EE000A798A /* NCShareLinkMenuView.swift in Sources */, F785EE9D246196DF00B3F945 /* NCNetworkingE2EE.swift in Sources */, F76673ED22C901F6007ED366 /* FileProviderDomain.swift in Sources */, F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */, @@ -2472,16 +2602,22 @@ F7A0D1352591FBC5008F8A13 /* String+Extensions.swift in Sources */, F77B0E5F1D118A16002130FE /* NCSettings.m in Sources */, F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */, + AF730AF827834B1400B7520E /* NCShare+NCCellDelegate.swift in Sources */, F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */, F78F74362163781100C2ADAD /* NCTrash.swift in Sources */, AF817EF1274BC781009ED85B /* NCUserBaseUrl.swift in Sources */, AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */, + 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 */, F702F2F725EE5CED008F8E80 /* NCLogin.swift in Sources */, + F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, + F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */, + AF93471A27E2361E002537EE /* NCShareAdvancePermissionHeader.swift in Sources */, F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */, F7DBC37C23325E02001A85BA /* NCAppConfigView.swift in Sources */, 3781B9B023DB2B7E006B4B1D /* AppDelegate+Menu.swift in Sources */, @@ -2494,6 +2630,7 @@ F7C1EEA525053A9C00866ACC /* NCDataSource.swift in Sources */, F713FF002472764100214AF6 /* UIImage+animatedGIF.m in Sources */, F749C10B23C4A5340027D966 /* NCIntroCollectionViewCell.swift in Sources */, + AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */, F718C24E254D507B00C5C256 /* NCViewerMediaDetailView.swift in Sources */, F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */, F78071091EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */, @@ -2501,12 +2638,13 @@ F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */, F765F73125237E3F00391DBE /* NCRecent.swift in Sources */, F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */, - F769454422E9F142000A798A /* NCShareUserMenuView.swift in Sources */, F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */, F738D4902756740100CD1D38 /* NCLoginNavigationController.swift in Sources */, F77B0E981D118A16002130FE /* CCManageAccount.m in Sources */, - F77EFC0C26D6751F00806ED6 /* NCShareQuickStatusMenu.swift in Sources */, + AF93474C27E34120002537EE /* NCUtility+Image.swift in Sources */, F702F30125EE5D2C008F8E80 /* NYMnemonic.m in Sources */, + AF93474E27E3F212002537EE /* NCShareNewUserAddComment.swift in Sources */, + AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */, F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */, F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */, AFD33240276A02C100F5AE02 /* UIApplication+Orientation.swift in Sources */, @@ -2522,6 +2660,7 @@ F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */, F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */, F70CAE3A1F8CF31A008125FD /* NCEndToEndEncryption.m in Sources */, + AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */, F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */, F74C0436253F1CDC009762AB /* NCShares.swift in Sources */, F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */, @@ -2543,6 +2682,7 @@ F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */, F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */, F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */, + F7F4F11027ECDC4A008676F9 /* UIDevice+Extensions.swift in Sources */, F77B0ED11D118A16002130FE /* Acknowledgements.m in Sources */, F70D8D8124A4A9BF000A5756 /* NCNetworkingProcessUpload.swift in Sources */, F7D96FCC246ED7E200536D73 /* NCNetworkingCheckRemoteUser.swift in Sources */, @@ -2843,6 +2983,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -2865,9 +3006,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 18; DEVELOPMENT_TEAM = 6JLRKY9ZV7; - ENABLE_BITCODE = NO; + ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; @@ -2888,11 +3029,12 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.3.1; + MARKETING_VERSION = 4.4.0; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) NC"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -2902,6 +3044,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; @@ -2924,9 +3067,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 18; DEVELOPMENT_TEAM = 6JLRKY9ZV7; - ENABLE_BITCODE = NO; + ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -2945,11 +3088,12 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.3.1; + MARKETING_VERSION = 4.4.0; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG NC"; + SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3072,12 +3216,20 @@ minimumVersion = 4.0.0; }; }; + F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/marinofaggiana/EasyTipView"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; F75E57A725BF0D61002B72C2 /* XCRemoteSwiftPackageReference "SVGKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SVGKit/SVGKit.git"; requirement = { - branch = 3.x; - kind = branch; + kind = upToNextMinorVersion; + minimumVersion = 3.0.0; }; }; F75EAED626D2552E00F4320E /* XCRemoteSwiftPackageReference "MarqueeLabel" */ = { @@ -3112,14 +3264,6 @@ kind = branch; }; }; - F76DA96A277B78400082465B /* XCRemoteSwiftPackageReference "FSCalendar" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/WenchaoD/FSCalendar"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.0; - }; - }; F76DA96D277B78AE0082465B /* XCRemoteSwiftPackageReference "TLPhotoPicker" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/tilltue/TLPhotoPicker"; @@ -3141,7 +3285,7 @@ repositoryURL = "https://github.com/nextcloud/ios-communication-library/"; requirement = { kind = exactVersion; - version = 0.99.5; + version = 0.99.6; }; }; F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { @@ -3256,6 +3400,11 @@ package = F74E771E277A2EF40013B958 /* XCRemoteSwiftPackageReference "XLForm" */; productName = XLForm; }; + F753BA92281FD8020015BFB6 /* EasyTipView */ = { + isa = XCSwiftPackageProductDependency; + package = F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */; + productName = EasyTipView; + }; F758A01127A7F03E0069468B /* JGProgressHUD */ = { isa = XCSwiftPackageProductDependency; package = F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */; @@ -3301,11 +3450,6 @@ package = F76DA967277B77E90082465B /* XCRemoteSwiftPackageReference "DropDown" */; productName = DropDown; }; - F76DA96B277B78400082465B /* FSCalendar */ = { - isa = XCSwiftPackageProductDependency; - package = F76DA96A277B78400082465B /* XCRemoteSwiftPackageReference "FSCalendar" */; - productName = FSCalendar; - }; F76DA96E277B78AE0082465B /* TLPhotoPicker */ = { isa = XCSwiftPackageProductDependency; package = F76DA96D277B78AE0082465B /* XCRemoteSwiftPackageReference "TLPhotoPicker" */; diff --git a/Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme b/Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme index 9f6feaf4e..32b1efb9e 100755 --- a/Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme +++ b/Nextcloud.xcodeproj/xcshareddata/xcschemes/File Provider Extension.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "1320" + LastUpgradeVersion = "1330" wasCreatedForAppExtension = "YES" version = "2.0"> <BuildAction diff --git a/Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme b/Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme index 3646e7016..5e0b54e5b 100755 --- a/Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme +++ b/Nextcloud.xcodeproj/xcshareddata/xcschemes/Nextcloud.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "1320" + LastUpgradeVersion = "1330" version = "1.7"> <BuildAction parallelizeBuildables = "YES" diff --git a/Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme b/Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme index 80e2013e6..13c5e2fe1 100755 --- a/Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme +++ b/Nextcloud.xcodeproj/xcshareddata/xcschemes/Share.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "1320" + LastUpgradeVersion = "1330" wasCreatedForAppExtension = "YES" version = "2.0"> <BuildAction diff --git a/NextcloudTests/SharePermissionTest.swift b/NextcloudTests/SharePermissionTest.swift new file mode 100644 index 000000000..919941eb4 --- /dev/null +++ b/NextcloudTests/SharePermissionTest.swift @@ -0,0 +1,136 @@ +// +// SharePermissionTest.swift +// Nextcloud +// +// Created by Henrik Storch on 29.03.22. +// Copyright © 2021 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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/>. +// + +@testable import Nextcloud +import XCTest +import NCCommunication + +class SharePermissionTest: XCTestCase { + override func setUp() { + let json = + """ + {"ocs":{"data":{"capabilities":{"files_sharing":{"default_permissions":31}}}}} + """.data(using: .utf8)! + NCManageDatabase.shared.addCapabilitiesJSon(json, account: "") + } + + func testShareCellPermissionCell() throws { + let share = NCTableShareOptions(sharee: NCCommunicationSharee(), metadata: tableMetadata(), password: nil) + let shareConfig = NCShareConfig(parentMetadata: tableMetadata(), share: share) + + for row in 0..<shareConfig.permissions.count { + guard let cell = shareConfig.config(for: IndexPath(row: row, section: 0)) as? NCToggleCellConfig else { + XCTFail("Invalid share permission cell") + continue + } + XCTAssertFalse(cell.isOn(for: share)) + } + + let meta = tableMetadata() + meta.sharePermissionsCollaborationServices = 31 + let fullShare = NCTableShareOptions(sharee: NCCommunicationSharee(), metadata: meta, password: nil) + let shareFullConfig = NCShareConfig(parentMetadata: meta, share: fullShare) + + for row in 0..<shareFullConfig.permissions.count { + guard let cell = shareConfig.config(for: IndexPath(row: row, section: 0)) as? NCToggleCellConfig else { + XCTFail("Invalid share permission cell") + continue + } + XCTAssertTrue(cell.isOn(for: fullShare)) + } + } + + func testSharePermission() throws { + XCTAssertTrue(NCLinkPermission.allowEdit.hasResharePermission(for: 15)) + XCTAssertTrue(NCLinkPermission.allowEdit.hasResharePermission(for: 11)) + XCTAssertTrue(NCLinkPermission.allowEdit.hasResharePermission(for: 7)) + XCTAssertFalse(NCLinkPermission.allowEdit.hasResharePermission(for: 13)) + XCTAssertFalse(NCLinkPermission.allowEdit.hasResharePermission(for: 1)) + + XCTAssertTrue(NCLinkPermission.viewOnly.hasResharePermission(for: 25)) + XCTAssertTrue(NCLinkPermission.viewOnly.hasResharePermission(for: 17)) + XCTAssertFalse(NCLinkPermission.viewOnly.hasResharePermission(for: 12)) + XCTAssertFalse(NCLinkPermission.viewOnly.hasResharePermission(for: 2)) + + XCTAssertTrue(NCLinkPermission.fileDrop.hasResharePermission(for: 4)) + XCTAssertFalse(NCLinkPermission.fileDrop.hasResharePermission(for: 27)) + + XCTAssertTrue(NCUserPermission.create.hasResharePermission(for: 4)) + XCTAssertFalse(NCUserPermission.create.hasResharePermission(for: 27)) + + XCTAssertTrue(NCUserPermission.edit.hasResharePermission(for: 2)) + XCTAssertFalse(NCUserPermission.edit.hasResharePermission(for: 29)) + + XCTAssertTrue(NCUserPermission.reshare.hasResharePermission(for: 16)) + XCTAssertFalse(NCUserPermission.reshare.hasResharePermission(for: 15)) + } + + func testFileShare() throws { + let meta = tableMetadata() + meta.directory = false + let share = NCTableShareOptions.shareLink(metadata: meta, password: nil) + let fileConfig = NCShareConfig(parentMetadata: meta, share: share) + XCTAssertEqual(fileConfig.advanced, NCShareDetails.forLink) + XCTAssertEqual(fileConfig.permissions as? [NCLinkPermission], NCLinkPermission.forFile) + + meta.directory = true + let folderConfig = NCShareConfig(parentMetadata: meta, share: share) + XCTAssertEqual(folderConfig.advanced, NCShareDetails.forLink) + XCTAssertEqual(folderConfig.permissions as? [NCLinkPermission], NCLinkPermission.forDirectory) + } + + func testUserShare() throws { + let meta = tableMetadata() + meta.directory = false + let sharee = NCCommunicationSharee() + let share = NCTableShareOptions(sharee: sharee, metadata: meta, password: nil) + let fileConfig = NCShareConfig(parentMetadata: meta, share: share) + XCTAssertEqual(fileConfig.advanced, NCShareDetails.forUser) + XCTAssertEqual(fileConfig.permissions as? [NCUserPermission], NCUserPermission.forFile) + + meta.directory = true + let folderConfig = NCShareConfig(parentMetadata: meta, share: share) + XCTAssertEqual(folderConfig.advanced, NCShareDetails.forUser) + XCTAssertEqual(folderConfig.permissions as? [NCUserPermission], NCUserPermission.forDirectory) + } + + func testResharePermission() throws { + let meta = tableMetadata() + let permissionReadShare = NCGlobal.shared.permissionShareShare + NCGlobal.shared.permissionReadShare + meta.sharePermissionsCollaborationServices = permissionReadShare + meta.directory = false + let share = NCTableShareOptions.shareLink(metadata: meta, password: nil) + let fileConfig = NCShareConfig(parentMetadata: meta, share: share) + XCTAssertEqual(fileConfig.resharePermission, meta.sharePermissionsCollaborationServices) + XCTAssertEqual(fileConfig.advanced, NCShareDetails.forLink) + XCTAssertEqual(fileConfig.permissions as? [NCLinkPermission], NCLinkPermission.forFile) + + meta.directory = true + let sharee = NCCommunicationSharee() + let folderShare = NCTableShareOptions(sharee: sharee, metadata: meta, password: nil) + let folderConfig = NCShareConfig(parentMetadata: meta, share: folderShare) + XCTAssertEqual(folderConfig.resharePermission, meta.sharePermissionsCollaborationServices) + XCTAssertEqual(folderConfig.advanced, NCShareDetails.forUser) + XCTAssertEqual(folderConfig.permissions as? [NCUserPermission], NCUserPermission.forDirectory) + } +} diff --git a/NextcloudTests/UserAgentTests.swift b/NextcloudTests/UserAgentTests.swift new file mode 100644 index 000000000..f66975c21 --- /dev/null +++ b/NextcloudTests/UserAgentTests.swift @@ -0,0 +1,64 @@ +// +// UserAgentTests.swift +// Nextcloud +// +// Created by Henrik Storch on 03.05.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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/>. +// + +@testable import Nextcloud +import XCTest + +class UserAgentTests: XCTestCase { + // https://github.com/nextcloud/server/blob/fc826e98115b510313ddacbf6fef4ce8d041e373/lib/public/IRequest.php#L83 + let ncServerUARegex = "^Mozilla\\/5\\.0 \\(iOS\\) (ownCloud|Nextcloud)\\-iOS.*$" + + // https://github.com/ProseMirror/prosemirror-view/blob/427d278aaaacde422ed1f2b8c84bb53337162775/src/browser.js#L18-L22 + let proseMirrorWebKitUARegex = "\\bAppleWebKit\\/(\\d+)" + let proseMirroriOSUARegex = "Mobile\\/\\w+" + + func testDefaultUserAgent() throws { + let userAgent: String = CCUtility.getUserAgent() + let match = try matches(for: ncServerUARegex, in: userAgent).first + XCTAssertNotNil(match) + } + + func testTextUserAgent() throws { + let userAgent: String = NCUtility.shared.getCustomUserAgentNCText() + let match = try matches(for: ncServerUARegex, in: userAgent).first + XCTAssertNotNil(match) + + let iOSMatch = try matches(for: proseMirroriOSUARegex, in: userAgent).first + XCTAssertNotNil(iOSMatch) + + // https://github.com/ProseMirror/prosemirror-view/blob/8f246f320801f8e3cac92c97f71ac91e3e327f2f/src/input.js#L521-L522 + let webKitMatch = try matches(for: proseMirrorWebKitUARegex, in: userAgent).first + XCTAssertNotNil(webKitMatch) + XCTAssertEqual(webKitMatch!.numberOfRanges, 2) + let versionRange = webKitMatch!.range(at: 1) + let versionString = userAgent[Range(versionRange, in: userAgent)!] + let webkitVersion = Int(versionString) ?? 0 + XCTAssertGreaterThanOrEqual(webkitVersion, 604) + } + + func matches(for regex: String, in text: String) throws -> [NSTextCheckingResult] { + let range = NSRange(location: 0, length: text.utf16.count) + let regex = try NSRegularExpression(pattern: regex) + return regex.matches(in: text, range: range) + } +} diff --git a/Share/NCShareExtension+Files.swift b/Share/NCShareExtension+Files.swift index 13f05e8dc..8a1ccf6bf 100644 --- a/Share/NCShareExtension+Files.swift +++ b/Share/NCShareExtension+Files.swift @@ -47,22 +47,15 @@ extension NCShareExtension { collectionView.reloadData() } - func createFolder(with fileName: String) { - - NCNetworking.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: activeAccount.account, urlBase: activeAccount.urlBase) { errorCode, errorDescription in - - DispatchQueue.main.async { - if errorCode == 0 { - - self.serverUrl += "/" + fileName - self.reloadDatasource(withLoadFolder: true) - self.setNavigationBar(navigationTitle: fileName) - - } else { - self.showAlert(title: "_error_createsubfolders_upload_", description: errorDescription) - } - } - } + @objc func didCreateFolder(_ notification: NSNotification) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + self.serverUrl += "/" + metadata.fileName + self.reloadDatasource(withLoadFolder: true) + self.setNavigationBar(navigationTitle: metadata.fileName) } func loadFolder() { diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift index f45c2d342..831dc455c 100644 --- a/Share/NCShareExtension.swift +++ b/Share/NCShareExtension.swift @@ -132,6 +132,7 @@ class NCShareExtension: UIViewController { } NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(didCreateFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil) } override func viewWillAppear(_ animated: Bool) { @@ -216,10 +217,7 @@ class NCShareExtension: UIViewController { } } - let image = NCUtility.shared.loadUserImage( - for: activeAccount.user, - displayName: activeAccount.displayName, - userBaseUrl: activeAccount) + let image = NCUtility.shared.loadUserImage(for: activeAccount.user, displayName: activeAccount.displayName, userBaseUrl: activeAccount) let profileButton = UIButton(type: .custom) profileButton.setImage(image, for: .normal) @@ -277,25 +275,11 @@ class NCShareExtension: UIViewController { } @objc func actionCreateFolder() { - - let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: "", preferredStyle: .alert) - - alertController.addTextField { textField in - textField.autocapitalizationType = UITextAutocapitalizationType.words - } - - let actionSave = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default) { _ in - if let fileName = alertController.textFields?.first?.text { - self.createFolder(with: fileName) - } + let alertController = UIAlertController.createFolder(serverUrl: serverUrl, urlBase: activeAccount) { errorCode, errorDescription in + guard errorCode != 0 else { return } + self.showAlert(title: "_error_createsubfolders_upload_", description: errorDescription) } - - let actionCancel = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) - - alertController.addAction(actionSave) - alertController.addAction(actionCancel) - - self.present(alertController, animated: true, completion: nil) + self.present(alertController, animated: true) } } @@ -359,16 +343,16 @@ extension NCShareExtension { hud.show(in: self.view) NCNetworking.shared.upload(metadata: metadata) { } completion: { errorCode, _ in - if errorCode == 0 { - self.counterUploaded += 1 - self.upload() - } else { + if errorCode != 0 { let path = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)! NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId) NCUtilityFileSystem.shared.deleteFile(filePath: path) self.uploadErrors.append(metadata) } + + self.counterUploaded += 1 + self.upload() } } diff --git a/iOSClient/Activity/NCActivity.storyboard b/iOSClient/Activity/NCActivity.storyboard index 39c8ecb24..6e9486484 100644 --- a/iOSClient/Activity/NCActivity.storyboard +++ b/iOSClient/Activity/NCActivity.storyboard @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="nhT-TJ-YvX"> +<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="nhT-TJ-YvX"> <device id="retina6_1" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -17,7 +17,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="20" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="X49-xg-JXO"> - <rect key="frame" x="0.0" y="100" width="414" height="762"/> + <rect key="frame" x="0.0" y="44" width="414" height="818"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="tableCell" rowHeight="120" id="ggj-aE-fnh" customClass="NCActivityTableViewCell" customModule="Nextcloud" customModuleProvider="target"> @@ -113,66 +113,18 @@ <outlet property="prefetchDataSource" destination="nhT-TJ-YvX" id="317-AD-uQe"/> </connections> </tableView> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sG1-7f-3rF"> - <rect key="frame" x="0.0" y="0.0" width="414" height="100"/> - <subviews> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hVn-Fn-7td"> - <rect key="frame" x="10" y="10" width="40" height="40"/> - <constraints> - <constraint firstAttribute="height" constant="40" id="eRU-q6-wZT"/> - <constraint firstAttribute="width" constant="40" id="nee-e2-atl"/> - </constraints> - </imageView> - <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="Wz7-gw-foA"> - <rect key="frame" x="60" y="60" width="344" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="4ni-Qx-ber"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="newCommentFieldDidEndOnExitWithTextField:" destination="nhT-TJ-YvX" eventType="editingDidEndOnExit" id="vPB-Eu-qkb"/> - </connections> - </textField> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="user" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YRy-AS-CMk"> - <rect key="frame" x="60" y="21.5" width="344" height="17"/> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <nil key="highlightedColor"/> - </label> - </subviews> - <constraints> - <constraint firstItem="YRy-AS-CMk" firstAttribute="centerY" secondItem="hVn-Fn-7td" secondAttribute="centerY" id="CBB-vi-6Z1"/> - <constraint firstAttribute="trailing" secondItem="Wz7-gw-foA" secondAttribute="trailing" constant="10" id="CuV-5o-sFz"/> - <constraint firstItem="hVn-Fn-7td" firstAttribute="leading" secondItem="sG1-7f-3rF" secondAttribute="leading" constant="10" id="NWH-NK-FjI"/> - <constraint firstAttribute="height" constant="100" id="SfP-Sr-vbR"/> - <constraint firstItem="Wz7-gw-foA" firstAttribute="leading" secondItem="hVn-Fn-7td" secondAttribute="trailing" constant="10" id="baP-t5-Kut"/> - <constraint firstItem="Wz7-gw-foA" firstAttribute="top" secondItem="hVn-Fn-7td" secondAttribute="bottom" constant="10" id="bsh-yh-NR2"/> - <constraint firstItem="YRy-AS-CMk" firstAttribute="leading" secondItem="hVn-Fn-7td" secondAttribute="trailing" constant="10" id="chn-JO-eYr"/> - <constraint firstAttribute="bottom" secondItem="Wz7-gw-foA" secondAttribute="bottom" constant="10" id="e8b-hy-WHK"/> - <constraint firstAttribute="trailing" secondItem="YRy-AS-CMk" secondAttribute="trailing" constant="10" id="uaN-5Y-k6V"/> - <constraint firstItem="hVn-Fn-7td" firstAttribute="top" secondItem="sG1-7f-3rF" secondAttribute="top" constant="10" id="yLz-68-e22"/> - </constraints> - </view> </subviews> <viewLayoutGuide key="safeArea" id="USa-eR-a1s"/> <constraints> - <constraint firstItem="sG1-7f-3rF" firstAttribute="top" secondItem="vOO-VC-ekK" secondAttribute="top" id="0Wu-9f-jFf"/> <constraint firstItem="X49-xg-JXO" firstAttribute="trailing" secondItem="USa-eR-a1s" secondAttribute="trailing" id="5we-Fh-GVu"/> + <constraint firstItem="X49-xg-JXO" firstAttribute="top" secondItem="USa-eR-a1s" secondAttribute="top" id="E1U-4Q-6uu"/> <constraint firstItem="USa-eR-a1s" firstAttribute="bottom" secondItem="X49-xg-JXO" secondAttribute="bottom" id="aHq-g4-dUG"/> - <constraint firstItem="X49-xg-JXO" firstAttribute="top" secondItem="sG1-7f-3rF" secondAttribute="bottom" id="eeu-9y-t1U"/> - <constraint firstItem="sG1-7f-3rF" firstAttribute="trailing" secondItem="vOO-VC-ekK" secondAttribute="trailing" id="htz-S1-01v"/> - <constraint firstItem="sG1-7f-3rF" firstAttribute="leading" secondItem="vOO-VC-ekK" secondAttribute="leading" id="lLm-NY-aXQ"/> <constraint firstItem="X49-xg-JXO" firstAttribute="leading" secondItem="USa-eR-a1s" secondAttribute="leading" id="pfF-ag-f7x"/> </constraints> </view> <connections> - <outlet property="commentView" destination="sG1-7f-3rF" id="Nip-au-Ilu"/> - <outlet property="imageItem" destination="hVn-Fn-7td" id="tqx-nV-WfA"/> - <outlet property="labelUser" destination="YRy-AS-CMk" id="ijz-je-fBV"/> - <outlet property="newCommentField" destination="Wz7-gw-foA" id="PDr-8b-iQY"/> <outlet property="tableView" destination="X49-xg-JXO" id="GUb-8b-mIS"/> - <outlet property="viewContainerConstraint" destination="0Wu-9f-jFf" id="TGF-fh-T7Y"/> + <outlet property="viewContainerConstraint" destination="E1U-4Q-6uu" id="NpJ-Iz-DtL"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="UOE-pW-DRy" userLabel="First Responder" sceneMemberID="firstResponder"/> diff --git a/iOSClient/Activity/NCActivity.swift b/iOSClient/Activity/NCActivity.swift index ab6354cfd..39edd3544 100644 --- a/iOSClient/Activity/NCActivity.swift +++ b/iOSClient/Activity/NCActivity.swift @@ -26,14 +26,12 @@ import UIKit import SwiftRichString import NCCommunication -class NCActivity: UIViewController { +class NCActivity: UIViewController, NCSharePagingContent { @IBOutlet weak var tableView: UITableView! - @IBOutlet weak var commentView: UIView! - @IBOutlet weak var imageItem: UIImageView! - @IBOutlet weak var labelUser: UILabel! - @IBOutlet weak var newCommentField: UITextField! + var commentView: NCActivityCommentView? + var textField: UITextField? { commentView?.newCommentField } @IBOutlet weak var viewContainerConstraint: NSLayoutConstraint! var height: CGFloat = 0 @@ -76,46 +74,34 @@ class NCActivity: UIViewController { if showComments { setupComments() - } else { - commentView.isHidden = true } } func setupComments() { - tableView.register(UINib(nibName: "NCShareCommentsCell", bundle: nil), forCellReuseIdentifier: "cell") - - newCommentField.placeholder = NSLocalizedString("_new_comment_", comment: "") - viewContainerConstraint.constant = height - // Display Name & Quota guard let activeAccount = NCManageDatabase.shared.getActiveAccount(), height > 0 else { - commentView.isHidden = true return } - let fileName = appDelegate.userBaseUrl + "-" + appDelegate.user + ".png" - let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName - if let image = UIImage(contentsOfFile: fileNameLocalPath) { - imageItem.image = image - } else { - imageItem.image = UIImage(named: "avatar") - } - - if activeAccount.displayName.isEmpty { - labelUser.text = activeAccount.user - } else { - labelUser.text = activeAccount.displayName + tableView.register(UINib(nibName: "NCShareCommentsCell", bundle: nil), forCellReuseIdentifier: "cell") + commentView = Bundle.main.loadNibNamed("NCActivityCommentView", owner: self, options: nil)?.first as? NCActivityCommentView + commentView?.setup(urlBase: appDelegate, account: activeAccount) { newComment in + guard let newComment = newComment, !newComment.isEmpty, let metadata = self.metadata else { return } + NCCommunication.shared.putComments(fileId: metadata.fileId, message: newComment) { _, errorCode, errorDescription in + if errorCode == 0 { + self.commentView?.newCommentField.text?.removeAll() + self.loadComments() + } else { + NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } + } } - labelUser.textColor = NCBrandColor.shared.label } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - appDelegate.activeViewController = self - NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil) - initialize() } @@ -127,6 +113,10 @@ class NCActivity: UIViewController { override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() tableView.tableFooterView = makeTableFooterView() + tableView.tableHeaderView = commentView + commentView?.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + commentView?.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + viewContainerConstraint.constant = height } // MARK: - NotificationCenter @@ -140,23 +130,6 @@ class NCActivity: UIViewController { tableView.reloadData() } - @IBAction func newCommentFieldDidEndOnExit(textField: UITextField) { - guard - let message = textField.text, - !message.isEmpty, - let metadata = self.metadata - else { return } - - NCCommunication.shared.putComments(fileId: metadata.fileId, message: message) { _, errorCode, errorDescription in - if errorCode == 0 { - self.newCommentField.text = "" - self.loadComments() - } else { - NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } - } - } - func makeTableFooterView() -> UIView { let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 100)) view.backgroundColor = .clear @@ -525,7 +498,7 @@ extension NCActivity: NCShareCommentsCellDelegate { actions.append( NCMenuAction( title: NSLocalizedString("_edit_comment_", comment: ""), - icon: UIImage(named: "edit")!.image(color: NCBrandColor.shared.gray, size: 50), + icon: UIImage(named: "pencil")!.image(color: NCBrandColor.shared.gray, size: 50), action: { _ in guard let metadata = self.metadata, let tableComments = tableComments else { return } diff --git a/iOSClient/Activity/NCActivityCommentView.swift b/iOSClient/Activity/NCActivityCommentView.swift new file mode 100644 index 000000000..b3b0d9c4f --- /dev/null +++ b/iOSClient/Activity/NCActivityCommentView.swift @@ -0,0 +1,59 @@ +// +// NCActivityCommentView.swift +// Nextcloud +// +// Created by Henrik Storch on 04.01.22. +// Copyright © 2021 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 NCActivityCommentView: UIView, UITextFieldDelegate { + @IBOutlet weak var imageItem: UIImageView! + @IBOutlet weak var labelUser: UILabel! + @IBOutlet weak var newCommentField: UITextField! + + var completionHandler: ((String?) -> Void)? + + func setup(urlBase: NCUserBaseUrl, account: tableAccount, completionHandler: @escaping (String?) -> Void) { + self.completionHandler = completionHandler + newCommentField.placeholder = NSLocalizedString("_new_comment_", comment: "") + newCommentField.delegate = self + + let fileName = urlBase.userBaseUrl + "-" + urlBase.user + ".png" + let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName + if let image = UIImage(contentsOfFile: fileNameLocalPath) { + imageItem.image = image + } else { + imageItem.image = UIImage(named: "avatar") + } + + if account.displayName.isEmpty { + labelUser.text = account.user + } else { + labelUser.text = account.displayName + } + labelUser.textColor = NCBrandColor.shared.label + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + completionHandler?(textField.text) + return true + } +} diff --git a/iOSClient/Activity/NCActivityCommentView.xib b/iOSClient/Activity/NCActivityCommentView.xib new file mode 100644 index 000000000..0eba994ad --- /dev/null +++ b/iOSClient/Activity/NCActivityCommentView.xib @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="retina6_1" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> + <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"/> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GuF-Pi-nHv" customClass="NCActivityCommentView" customModule="Nextcloud" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="269" height="100"/> + <subviews> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YXy-gE-g7y"> + <rect key="frame" x="10" y="10" width="40" height="40"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="kUz-t2-bFL"/> + <constraint firstAttribute="height" constant="40" id="yRS-7c-bMw"/> + </constraints> + </imageView> + <textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="5pv-VB-vbL"> + <rect key="frame" x="60" y="60" width="199" height="30"/> + <constraints> + <constraint firstAttribute="height" constant="30" id="OLX-lD-EIH"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="14"/> + <textInputTraits key="textInputTraits"/> + </textField> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="user" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Ja-ik-S0n"> + <rect key="frame" x="60" y="21.5" width="199" height="17"/> + <fontDescription key="fontDescription" type="system" pointSize="14"/> + <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <viewLayoutGuide key="safeArea" id="Y5x-Vi-PYA"/> + <constraints> + <constraint firstItem="YXy-gE-g7y" firstAttribute="top" secondItem="GuF-Pi-nHv" secondAttribute="top" constant="10" id="26g-rF-Ags"/> + <constraint firstAttribute="trailing" secondItem="0Ja-ik-S0n" secondAttribute="trailing" constant="10" id="7ue-4o-ZT2"/> + <constraint firstAttribute="height" constant="100" id="IsL-V9-dXU"/> + <constraint firstItem="0Ja-ik-S0n" firstAttribute="centerY" secondItem="YXy-gE-g7y" secondAttribute="centerY" id="NxM-vu-j06"/> + <constraint firstItem="5pv-VB-vbL" firstAttribute="leading" secondItem="YXy-gE-g7y" secondAttribute="trailing" constant="10" id="Oza-Za-mDZ"/> + <constraint firstItem="5pv-VB-vbL" firstAttribute="top" secondItem="YXy-gE-g7y" secondAttribute="bottom" constant="10" id="iie-Nv-YUr"/> + <constraint firstItem="0Ja-ik-S0n" firstAttribute="leading" secondItem="YXy-gE-g7y" secondAttribute="trailing" constant="10" id="j0L-NP-Z4H"/> + <constraint firstAttribute="trailing" secondItem="5pv-VB-vbL" secondAttribute="trailing" constant="10" id="oXJ-ov-XCK"/> + <constraint firstItem="YXy-gE-g7y" firstAttribute="leading" secondItem="GuF-Pi-nHv" secondAttribute="leading" constant="10" id="t5p-fd-swt"/> + <constraint firstAttribute="bottom" secondItem="5pv-VB-vbL" secondAttribute="bottom" constant="10" id="yEr-QL-mtD"/> + </constraints> + <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> + <connections> + <outlet property="imageItem" destination="YXy-gE-g7y" id="yWc-3P-gIU"/> + <outlet property="labelUser" destination="0Ja-ik-S0n" id="GkS-TV-2ic"/> + <outlet property="newCommentField" destination="5pv-VB-vbL" id="8vL-Mt-0rZ"/> + </connections> + <point key="canvasLocation" x="-231.15942028985509" y="-99.776785714285708"/> + </view> + </objects> +</document> diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index 7172cc047..ccaeb5659 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -188,12 +188,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // L' applicazione entrerà in primo piano (attivo sempre) func applicationDidBecomeActive(_ application: UIApplication) { - - // Privacy - hidePrivacyProtectionWindow() - - NCSettingsBundleHelper.setVersionAndBuildNumber() - + + if !NCAskAuthorization.shared.isRequesting { + // Privacy + hidePrivacyProtectionWindow() + } + NCSettingsBundleHelper.setVersionAndBuildNumber() if account == "" { return } @@ -245,9 +245,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if account == "" { return } - // Privacy - showPrivacyProtectionWindow() - + if CCUtility.getPrivacyScreenEnabled() { + // Privacy + showPrivacyProtectionWindow() + } + // Clear operation queue NCOperationQueue.shared.cancelAllQueue() // Clear download @@ -266,20 +268,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func applicationDidEnterBackground(_ application: UIApplication) { if account == "" { return } - + // STOP TIMER UPLOAD PROCESS if NCUtility.shared.isSimulator() { networkingProcessUpload?.stopTimer() } - + if #available(iOS 13.0, *) { scheduleAppRefresh() scheduleBackgroundProcessing() } - + // Passcode presentPasscode { } - + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidEnterBackground) } @@ -297,7 +299,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if account == "" { return } NCCommunicationCommon.shared.writeLog("initialize Main") - + // Registeration push notification NCPushNotification.shared().pushNotification() @@ -362,7 +364,7 @@ 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 %lu uploads [Auto upload]") + NCCommunicationCommon.shared.writeLog("Completition handler refresh task with [Auto upload]") task.setTaskCompleted(success: true) } } @@ -606,6 +608,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD if serverVersionMajor > 0 { NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor) } + NCKTVHTTPCache.shared.restartProxy(user: user, password: password) } @objc func deleteAccount(_ account: String, wipe: Bool) { @@ -654,17 +657,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - Account Request func accountRequestChangeAccount(account: String) { - changeAccount(account) } func requestAccount() { - + if isPasscodePresented() { return } if !CCUtility.getAccountRequest() { return } - + let accounts = NCManageDatabase.shared.getAllAccount() - + if accounts.count > 1 { if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest { @@ -691,30 +693,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } // MARK: - Passcode - - func presentPasscode(completion: @escaping ()->()) { + + func presentPasscode(completion: @escaping () -> ()) { let laContext = LAContext() var error: NSError? - defer { - self.requestAccount() - } - - guard !account.isEmpty, CCUtility.isPasscodeAtStartEnabled() else { return } - - // If activated hide the privacy protection - hidePrivacyProtectionWindow() + defer { self.requestAccount() } - // Dismiss present window?.rootViewController? [ONLY PASSCODE] let presentedViewController = window?.rootViewController?.presentedViewController - if presentedViewController is NCLoginNavigationController { - return - } else { - presentedViewController?.dismiss(animated: false) - } + guard !account.isEmpty, CCUtility.isPasscodeAtStartEnabled(), !(presentedViewController is NCLoginNavigationController) else { return } - let passcodeViewController = TOPasscodeViewController.init(passcodeType: .sixDigits, allowCancel: false) + // Make sure we have a privacy window (in case it's not enabled) + showPrivacyProtectionWindow() + + let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: false) passcodeViewController.delegate = self passcodeViewController.keypadButtonShowLettering = false if CCUtility.getEnableTouchFaceID() && laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { @@ -728,38 +721,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD passcodeViewController.automaticallyPromptForBiometricValidation = false } } - - window?.rootViewController?.present(passcodeViewController, animated: true, completion: { + + // show passcode on top of privacy window + privacyProtectionWindow?.rootViewController?.present(passcodeViewController, animated: true, completion: { completion() }) } - + func isPasscodePresented() -> Bool { - return window?.rootViewController?.presentedViewController is TOPasscodeViewController + return privacyProtectionWindow?.rootViewController?.presentedViewController is TOPasscodeViewController } - - func enableTouchFaceID() { + func enableTouchFaceID() { guard !account.isEmpty, CCUtility.getEnableTouchFaceID(), CCUtility.isPasscodeAtStartEnabled(), - let passcodeViewController = window?.rootViewController?.presentedViewController as? TOPasscodeViewController + let passcodeViewController = privacyProtectionWindow?.rootViewController?.presentedViewController as? TOPasscodeViewController else { return } LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { (success, error) in if success { DispatchQueue.main.async { passcodeViewController.dismiss(animated: true) { + self.hidePrivacyProtectionWindow() self.requestAccount() } } } } } - + func didInputCorrectPasscode(in passcodeViewController: TOPasscodeViewController) { DispatchQueue.main.async { passcodeViewController.dismiss(animated: true) { + self.hidePrivacyProtectionWindow() self.requestAccount() } } @@ -769,27 +764,37 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return code == CCUtility.getPasscode() } + func didPerformBiometricValidationRequest(in passcodeViewController: TOPasscodeViewController) { + enableTouchFaceID() + } + // MARK: - Privacy Protection - + private func showPrivacyProtectionWindow() { - - guard CCUtility.getPrivacyScreenEnabled() else { return } - + guard privacyProtectionWindow == nil else { + privacyProtectionWindow?.isHidden = false + return + } + privacyProtectionWindow = UIWindow(frame: UIScreen.main.bounds) - + let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil) let initialViewController = storyboard.instantiateInitialViewController() self.privacyProtectionWindow?.rootViewController = initialViewController - + privacyProtectionWindow?.windowLevel = .alert + 1 privacyProtectionWindow?.makeKeyAndVisible() } - private func hidePrivacyProtectionWindow() { - - privacyProtectionWindow?.isHidden = true - privacyProtectionWindow = nil + func hidePrivacyProtectionWindow() { + guard !(privacyProtectionWindow?.rootViewController?.presentedViewController is TOPasscodeViewController) else { return } + UIWindow.animate(withDuration: 0.25) { + self.privacyProtectionWindow?.alpha = 0 + } completion: { _ in + self.privacyProtectionWindow?.isHidden = true + self.privacyProtectionWindow = nil + } } // MARK: - Open URL @@ -873,13 +878,13 @@ extension AppDelegate: NCAudioRecorderViewControllerDelegate { func didFinishRecording(_ viewController: NCAudioRecorderViewController, fileName: String) { - guard let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() else { return } - navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet - let appDelegate = UIApplication.shared.delegate as! AppDelegate - - let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadVoiceNote - viewController.setup(serverUrl: appDelegate.activeServerUrl, fileNamePath: NSTemporaryDirectory() + fileName, fileName: fileName) - appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil) + guard + let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController, + let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote + else { return } + navigationController.modalPresentationStyle = .formSheet + viewController.setup(serverUrl: activeServerUrl, fileNamePath: NSTemporaryDirectory() + fileName, fileName: fileName) + window?.rootViewController?.present(navigationController, animated: true) } func didFinishWithoutRecording(_ viewController: NCAudioRecorderViewController, fileName: String) { diff --git a/iOSClient/Brand/Intro/NCIntroCollectionViewCell.xib b/iOSClient/Brand/Intro/NCIntroCollectionViewCell.xib index 5c2357315..7eed66d3b 100644 --- a/iOSClient/Brand/Intro/NCIntroCollectionViewCell.xib +++ b/iOSClient/Brand/Intro/NCIntroCollectionViewCell.xib @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" 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="retina6_1" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/> + <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> @@ -18,32 +18,30 @@ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bZc-Ai-h3y"> - <rect key="frame" x="8" y="426" width="321" height="28"/> - <constraints> - <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="28" id="bXT-nO-EeQ"/> - </constraints> + <rect key="frame" x="8" y="426.5" width="321" height="27.5"/> <fontDescription key="fontDescription" type="system" pointSize="23"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" image="intro1" translatesAutoresizingMaskIntoConstraints="NO" id="f3T-nC-cwA"> - <rect key="frame" x="93.5" y="8" width="150" height="410"/> + <rect key="frame" x="93.5" y="156" width="150" height="150"/> <constraints> <constraint firstAttribute="width" relation="lessThanOrEqual" constant="150" id="jne-Xj-IAh"/> + <constraint firstAttribute="width" secondItem="f3T-nC-cwA" secondAttribute="height" multiplier="1:1" id="mhZ-Cn-42Q"/> </constraints> </imageView> </subviews> </view> + <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/> <color key="backgroundColor" red="1" green="0.57637232540000005" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstItem="f3T-nC-cwA" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="4le-ih-K34"/> - <constraint firstItem="bZc-Ai-h3y" firstAttribute="top" secondItem="f3T-nC-cwA" secondAttribute="bottom" constant="8" id="AWt-Zg-Blt"/> - <constraint firstItem="f3T-nC-cwA" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="8" id="Eh0-Bq-FKp"/> <constraint firstAttribute="bottom" secondItem="bZc-Ai-h3y" secondAttribute="bottom" constant="8" id="YBJ-eg-EmZ"/> + <constraint firstItem="bZc-Ai-h3y" firstAttribute="top" relation="greaterThanOrEqual" secondItem="f3T-nC-cwA" secondAttribute="bottom" constant="16" id="av5-zS-GLQ"/> <constraint firstItem="bZc-Ai-h3y" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="8" id="eNj-fx-een"/> + <constraint firstItem="f3T-nC-cwA" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" priority="250" id="haX-eS-EOe"/> <constraint firstAttribute="trailing" secondItem="bZc-Ai-h3y" secondAttribute="trailing" constant="8" id="uif-cW-sAI"/> </constraints> - <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/> <size key="customSize" width="337" height="428"/> <connections> <outlet property="imageView" destination="f3T-nC-cwA" id="aRR-4x-Dwk"/> @@ -53,6 +51,6 @@ </collectionViewCell> </objects> <resources> - <image name="intro1" width="200" height="200"/> + <image name="intro1" width="256" height="128"/> </resources> </document> diff --git a/iOSClient/Brand/Intro/NCIntroViewController.swift b/iOSClient/Brand/Intro/NCIntroViewController.swift index f9e725dc8..1002b888c 100644 --- a/iOSClient/Brand/Intro/NCIntroViewController.swift +++ b/iOSClient/Brand/Intro/NCIntroViewController.swift @@ -89,6 +89,8 @@ class NCIntroViewController: UIViewController, UICollectionViewDataSource, UICol buttonSignUp.layer.borderWidth = 1.0 buttonSignUp.setTitleColor(textColor, for: .normal) buttonSignUp.backgroundColor = NCBrandColor.shared.customer + buttonSignUp.titleLabel?.adjustsFontSizeToFitWidth = true + buttonSignUp.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) buttonSignUp.setTitle(NSLocalizedString("_sign_up_", comment: ""), for: .normal) buttonHost.layer.cornerRadius = 20 diff --git a/iOSClient/Brand/LaunchScreen.storyboard b/iOSClient/Brand/LaunchScreen.storyboard index 0a2323f9f..26840f619 100755 --- a/iOSClient/Brand/LaunchScreen.storyboard +++ b/iOSClient/Brand/LaunchScreen.storyboard @@ -1,11 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> - <device id="retina4_7" orientation="portrait"> - <adaptation id="fullscreen"/> - </device> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> + <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="19519"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -18,16 +16,18 @@ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="7UE-Dr-Fma"> - <rect key="frame" x="134" y="306" width="107" height="75"/> + <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="7UE-Dr-Fma"> + <rect key="frame" x="59.5" y="269.5" width="256" height="128"/> </imageView> </subviews> + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> <color key="backgroundColor" red="0.0" green="0.50980392156862742" blue="0.78823529411764703" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> + <constraint firstItem="7UE-Dr-Fma" firstAttribute="width" relation="lessThanOrEqual" secondItem="6Tk-OE-BBY" secondAttribute="width" id="8ik-eB-po8"/> + <constraint firstItem="7UE-Dr-Fma" firstAttribute="height" relation="lessThanOrEqual" secondItem="6Tk-OE-BBY" secondAttribute="height" id="G3L-l7-lxv"/> <constraint firstItem="7UE-Dr-Fma" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="XdS-tM-D9I"/> <constraint firstItem="7UE-Dr-Fma" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" id="ZR5-ct-Gg8"/> </constraints> - <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> </view> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> @@ -36,6 +36,6 @@ </scene> </scenes> <resources> - <image name="logo" width="107" height="75.599998474121094"/> + <image name="logo" width="256" height="128"/> </resources> </document> diff --git a/iOSClient/Brand/NCBrand.swift b/iOSClient/Brand/NCBrand.swift index f393126b4..b8148e5af 100755 --- a/iOSClient/Brand/NCBrand.swift +++ b/iOSClient/Brand/NCBrand.swift @@ -47,7 +47,7 @@ import UIKit @objc public var brand: String = "Nextcloud" // @objc public var mailMe: String = "ios@nextcloud.com" // Deprecated - @objc public var textCopyrightNextcloudiOS: String = "Nextcloud Liquid for iOS %@ © 2021" + @objc public var textCopyrightNextcloudiOS: String = "Nextcloud Liquid for iOS %@ © 2022" @objc public var textCopyrightNextcloudServer: String = "Nextcloud Server %@" @objc public var loginBaseUrl: String = "https://cloud.nextcloud.com" @objc public var pushNotificationServerProxy: String = "https://push-notifications.nextcloud.com" @@ -137,6 +137,7 @@ class NCBrandColor: NSObject { static var buttonMore = UIImage() static var buttonStop = UIImage() + static var buttonMoreLock = UIImage() static var buttonRestore = UIImage() } @@ -215,6 +216,16 @@ class NCBrandColor: NSObject { } } + @objc public var secondaryLabel: UIColor { + get { + if #available(iOS 13, *) { + return .secondaryLabel + } else { + return UIColor(red: 0.24, green: 0.24, blue: 0.26, alpha: 0.6) + } + } + } + @objc public var separator: UIColor { get { if #available(iOS 13, *) { @@ -345,6 +356,7 @@ class NCBrandColor: NSObject { cacheImages.buttonMore = UIImage(named: "more")!.image(color: gray, size: 50) 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) } diff --git a/iOSClient/Brand/iOSClient.entitlements b/iOSClient/Brand/iOSClient.entitlements index 1f984963f..a879c6400 100755 --- a/iOSClient/Brand/iOSClient.entitlements +++ b/iOSClient/Brand/iOSClient.entitlements @@ -4,18 +4,6 @@ <dict> <key>aps-environment</key> <string>development</string> - <key>com.apple.developer.icloud-container-identifiers</key> - <array> - <string>iCloud.$(CFBundleIdentifier)</string> - </array> - <key>com.apple.developer.icloud-services</key> - <array> - <string>CloudDocuments</string> - </array> - <key>com.apple.developer.ubiquity-container-identifiers</key> - <array> - <string>iCloud.$(CFBundleIdentifier)</string> - </array> <key>com.apple.security.application-groups</key> <array> <string>group.it.twsweb.Crypto-Cloud</string> diff --git a/iOSClient/Brand/iOSClient.plist b/iOSClient/Brand/iOSClient.plist index 9af7f687d..9f096d6cb 100755 --- a/iOSClient/Brand/iOSClient.plist +++ b/iOSClient/Brand/iOSClient.plist @@ -64,17 +64,29 @@ <key>NSFaceIDUsageDescription</key> <string>Face ID is required to authenticate using face recognition.</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> - <string>GPS is used to detect new photos from camera roll, continued use of GPS running in the background can dramatically decrease battery life.</string> + <string>GPS is used to detect new photos from camera roll. Continued use of GPS running in the background can dramatically decrease battery life.</string> <key>NSLocationAlwaysUsageDescription</key> - <string>GPS is used to detect new photos from camera roll, continued use of GPS running in the background can dramatically decrease battery life.</string> + <string>GPS is used to detect new photos from camera roll. Continued use of GPS running in the background can dramatically decrease battery life.</string> <key>NSLocationWhenInUseUsageDescription</key> - <string>GPS is used to detect new photos from camera roll on background, the use of GPS only when the App is in use is useless.</string> + <string>GPS is used to detect new photos from camera roll on background. It is useless to use GPS only while using the app.</string> <key>NSMicrophoneUsageDescription</key> <string>Microphone access is required to create voice notes.</string> <key>NSPhotoLibraryAddUsageDescription</key> <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>UIAppFonts</key> + <array> + <string>Inconsolata-Light.ttf</string> + <string>Inconsolata-Regular.ttf</string> + <string>Inconsolata-ExtraLight.ttf</string> + <string>Inconsolata-Medium.ttf</string> + <string>Inconsolata-Bold.ttf</string> + <string>Inconsolata-ExtraBold.ttf</string> + <string>Inconsolata-Black.ttf</string> + </array> + <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key> + <true/> <key>UIBackgroundModes</key> <array> <string>audio</string> @@ -105,8 +117,6 @@ <string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationPortraitUpsideDown</string> </array> - <key>UISupportsDocumentBrowser</key> - <true/> <key>UIViewControllerBasedStatusBarAppearance</key> <true/> <key>UTExportedTypeDeclarations</key> @@ -132,6 +142,27 @@ </array> </dict> </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.text</string> + </array> + <key>UTTypeDescription</key> + <string>SRT Subtitle Format</string> + <key>UTTypeIconFiles</key> + <array/> + <key>UTTypeIdentifier</key> + <string>com.company.srt</string> + <key>UTTypeReferenceURL</key> + <string></string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>srt</string> + </array> + </dict> + </dict> </array> </dict> </plist> diff --git a/iOSClient/Data/NCDatabase.swift b/iOSClient/Data/NCDatabase.swift index 479e8ca92..0bd307150 100644 --- a/iOSClient/Data/NCDatabase.swift +++ b/iOSClient/Data/NCDatabase.swift @@ -37,9 +37,7 @@ class tableAccount: Object, NCUserBaseUrl { @objc dynamic var address = "" @objc dynamic var alias = "" @objc dynamic var autoUpload: Bool = false - @objc dynamic var autoUploadBackground: Bool = false @objc dynamic var autoUploadCreateSubfolder: Bool = false - @objc dynamic var autoUploadDeleteAssetLocalIdentifier: Bool = false @objc dynamic var autoUploadDirectory = "" @objc dynamic var autoUploadFileName = "" @objc dynamic var autoUploadFull: Bool = false @@ -384,6 +382,13 @@ class tableMetadata: Object, NCUserBaseUrl { @objc dynamic var ocId = "" @objc dynamic var ownerId = "" @objc dynamic var ownerDisplayName = "" + @objc public var lock = false + @objc public var lockOwner = "" + @objc public var lockOwnerEditor = "" + @objc public var lockOwnerType = 0 + @objc public var lockOwnerDisplayName = "" + @objc public var lockTime: Date? + @objc public var lockTimeOut: Date? @objc dynamic var path = "" @objc dynamic var permissions = "" @objc dynamic var quotaUsedBytes: Int64 = 0 @@ -420,6 +425,11 @@ extension tableMetadata { var isPrintable: Bool { classFile == NCCommunicationCommon.typeClassFile.image.rawValue || ["application/pdf", "com.adobe.pdf"].contains(contentType) || contentType.hasPrefix("text/") } + + /// Returns false if the user is lokced out of the file. I.e. The file is locked but by somone else + func canUnlock(as user: String) -> Bool { + return !lock || (lockOwner == user && lockOwnerType == 0) + } } class tablePhotoLibrary: Object { @@ -493,6 +503,11 @@ class tableTag: Object { } } +class tableTip: Object { + + @Persisted(primaryKey: true) var tipName = "" +} + class tableTrash: Object { @objc dynamic var account = "" @@ -539,7 +554,6 @@ class tableVideo: Object { @objc dynamic var codecNameAudio: String? @objc dynamic var codecAudioChannelLayout: String? @objc dynamic var codecAudioLanguage: String? - @objc dynamic var codecSubtitleLanguage: String? @objc dynamic var codecMaxCompatibility: Bool = false @objc dynamic var codecQuality: String? diff --git a/iOSClient/Data/NCElementsJSON.swift b/iOSClient/Data/NCElementsJSON.swift index 030464637..962a142c3 100644 --- a/iOSClient/Data/NCElementsJSON.swift +++ b/iOSClient/Data/NCElementsJSON.swift @@ -58,7 +58,8 @@ import UIKit @objc public let capabilitiesNotification: Array = ["ocs", "data", "capabilities", "notifications", "ocs-endpoints"] @objc public let capabilitiesFilesUndelete: Array = ["ocs", "data", "capabilities", "files", "undelete"] - @objc public let capabilitiesFilesComments: Array = ["ocs", "data", "capabilities", "files", "comments"] // NC 20 + @objc public let capabilitiesFilesLockVersion: Array = ["ocs", "data", "capabilities", "files", "locking"] // NC 24 + @objc public let capabilitiesFilesComments: Array = ["ocs", "data", "capabilities", "files", "comments"] // NC 20 @objc public let capabilitiesHWCEnabled: Array = ["ocs", "data", "capabilities", "handwerkcloud", "enabled"] diff --git a/iOSClient/Data/NCManageDatabase+Account.swift b/iOSClient/Data/NCManageDatabase+Account.swift index 305e37f33..7b5574ea2 100644 --- a/iOSClient/Data/NCManageDatabase+Account.swift +++ b/iOSClient/Data/NCManageDatabase+Account.swift @@ -5,6 +5,21 @@ // Created by Henrik Storch on 30.11.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 Foundation import RealmSwift diff --git a/iOSClient/Data/NCManageDatabase+Activity.swift b/iOSClient/Data/NCManageDatabase+Activity.swift index f3d7f3a1f..3e84bf4df 100644 --- a/iOSClient/Data/NCManageDatabase+Activity.swift +++ b/iOSClient/Data/NCManageDatabase+Activity.swift @@ -5,6 +5,21 @@ // Created by Henrik Storch on 30.11.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 Foundation import RealmSwift @@ -13,9 +28,6 @@ import SwiftyJSON extension NCManageDatabase { - // MARK: - - // MARK: Table Activity - @objc func addActivity(_ activities: [NCCommunicationActivity], account: String) { let realm = try! Realm() diff --git a/iOSClient/Data/NCManageDatabse+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift index 684a37a1e..2c115ce6d 100644 --- a/iOSClient/Data/NCManageDatabse+Metadata.swift +++ b/iOSClient/Data/NCManageDatabase+Metadata.swift @@ -1,10 +1,25 @@ // -// NCManageDatabse+Metadata.swift +// NCManageDatabase+Metadata.swift // Nextcloud // // Created by Henrik Storch on 30.11.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 Foundation import RealmSwift @@ -49,6 +64,13 @@ extension NCManageDatabase { metadata.ocId = file.ocId metadata.ownerId = file.ownerId metadata.ownerDisplayName = file.ownerDisplayName + metadata.lock = file.lock + metadata.lockOwner = file.lockOwner + metadata.lockOwnerEditor = file.lockOwnerEditor + metadata.lockOwnerType = file.lockOwnerType + metadata.lockOwnerDisplayName = file.lockOwnerDisplayName + metadata.lockTime = file.lockTime + metadata.lockTimeOut = file.lockTimeOut metadata.path = file.path metadata.permissions = file.permissions metadata.quotaUsedBytes = file.quotaUsedBytes @@ -65,6 +87,10 @@ 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 { + metadata.classFile = NCCommunicationCommon.typeClassFile.document.rawValue + } if let date = file.uploadDate { metadata.uploadDate = date } else { @@ -166,6 +192,10 @@ extension NCManageDatabase { metadata.urlBase = urlBase metadata.user = user metadata.userId = userId + + if !metadata.urlBase.isEmpty, metadata.serverUrl.hasPrefix(metadata.urlBase) { + metadata.path = String(metadata.serverUrl.dropFirst(metadata.urlBase.count)) + "/" + } return metadata } @@ -300,7 +330,9 @@ extension NCManageDatabase { if let result = metadatasResult.first(where: { $0.ocId == metadata.ocId }) { // update - 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) { + // 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) { ocIdsUdate.append(metadata.ocId) realm.add(tableMetadata.init(value: metadata), update: .all) } else if result.status == NCGlobal.shared.metadataStatusNormal && addCompareLivePhoto && result.livePhoto != metadata.livePhoto { @@ -676,14 +708,14 @@ extension NCManageDatabase { } } - @objc func getAssetLocalIdentifiersUploaded(account: String, sessionSelector: String) -> [String] { + @objc func getAssetLocalIdentifiersUploaded(account: String) -> [String] { let realm = try! Realm() realm.refresh() var assetLocalIdentifiers: [String] = [] - let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != '' AND deleteAssetLocalIdentifier == true AND sessionSelector == %@", account, sessionSelector) + let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != '' AND deleteAssetLocalIdentifier == true", account) for result in results { assetLocalIdentifiers.append(result.assetLocalIdentifier) } @@ -788,4 +820,19 @@ extension NCManageDatabase { } return getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", account, serverUrl, fileNameConflict)) } + + func getSubtitles(account: String, serverUrl: String, fileName: String) -> (all:[tableMetadata], existing:[tableMetadata]) { + + let realm = try! Realm() + let nameOnly = (fileName as NSString).deletingPathExtension + "." + var metadatas: [tableMetadata] = [] + + let results = realm.objects(tableMetadata.self).filter("account == %@ AND serverUrl == %@ AND fileName BEGINSWITH[c] %@ AND fileName ENDSWITH[c] '.srt'", account, serverUrl, nameOnly) + for result in results { + if CCUtility.fileProviderStorageExists(result) { + metadatas.append(result) + } + } + return(Array(results.map { tableMetadata.init(value: $0) }), Array(metadatas.map { tableMetadata.init(value: $0) })) + } } diff --git a/iOSClient/Data/NCManageDatabase+Video.swift b/iOSClient/Data/NCManageDatabase+Video.swift new file mode 100644 index 000000000..34723d9ec --- /dev/null +++ b/iOSClient/Data/NCManageDatabase+Video.swift @@ -0,0 +1,152 @@ +// +// NCManageDatabase+Video.swift +// Nextcloud +// +// Created by Marino Faggiana on 15/03/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 RealmSwift +import NCCommunication + +extension NCManageDatabase { + + func addVideoTime(metadata: tableMetadata, time: CMTime?, durationTime: CMTime?) { + + if metadata.livePhoto { return } + let realm = try! Realm() + + do { + try realm.safeWrite { + if let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first { + + if let durationTime = durationTime { + result.duration = durationTime.convertScale(1000, method: .default).value + } + if let time = time { + result.time = time.convertScale(1000, method: .default).value + } + realm.add(result, update: .all) + + } else { + + let addObject = tableVideo() + + addObject.account = metadata.account + if let durationTime = durationTime { + addObject.duration = durationTime.convertScale(1000, method: .default).value + } + addObject.ocId = metadata.ocId + if let time = time { + addObject.time = time.convertScale(1000, method: .default).value + } + realm.add(addObject, update: .all) + } + } + } catch let error { + NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + } + } + + func addVideoCodec(metadata: tableMetadata, codecNameVideo: String?, codecNameAudio: String?, codecAudioChannelLayout: String?, codecAudioLanguage: String?, codecMaxCompatibility: Bool, codecQuality: String?) { + + let realm = try! Realm() + + do { + try realm.safeWrite { + if let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first { + if let codecNameVideo = codecNameVideo { result.codecNameVideo = codecNameVideo } + if let codecNameAudio = codecNameAudio { result.codecNameAudio = codecNameAudio } + if let codecAudioChannelLayout = codecAudioChannelLayout { result.codecAudioChannelLayout = codecAudioChannelLayout } + if let codecAudioLanguage = codecAudioLanguage { result.codecAudioLanguage = codecAudioLanguage } + result.codecMaxCompatibility = codecMaxCompatibility + if let codecQuality = codecQuality { result.codecQuality = codecQuality } + realm.add(result, update: .all) + } else { + let addObject = tableVideo() + addObject.account = metadata.account + addObject.ocId = metadata.ocId + addObject.codecNameVideo = codecNameVideo + addObject.codecNameAudio = codecNameAudio + addObject.codecAudioChannelLayout = codecAudioChannelLayout + addObject.codecAudioLanguage = codecAudioLanguage + addObject.codecMaxCompatibility = codecMaxCompatibility + addObject.codecQuality = codecQuality + realm.add(addObject, update: .all) + } + } + } catch let error { + NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + } + } + + func getVideo(metadata: tableMetadata?) -> tableVideo? { + guard let metadata = metadata else { return nil } + + let realm = try! Realm() + guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { + return nil + } + + return tableVideo.init(value: result) + } + + func getVideoDurationTime(metadata: tableMetadata?) -> CMTime? { + guard let metadata = metadata else { return nil } + + if metadata.livePhoto { return nil } + let realm = try! Realm() + + guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { + return nil + } + + if result.duration == 0 { return nil } + let duration = CMTimeMake(value: result.duration, timescale: 1000) + return duration + } + + func getVideoTime(metadata: tableMetadata) -> CMTime? { + + if metadata.livePhoto { return nil } + let realm = try! Realm() + + guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { + return nil + } + + if result.time == 0 { return nil } + let time = CMTimeMake(value: result.time, timescale: 1000) + return time + } + + func deleteVideo(metadata: tableMetadata) { + + let realm = try! Realm() + + do { + try realm.safeWrite { + let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId) + realm.delete(result) + } + } catch let error { + NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + } + } +} diff --git a/iOSClient/Data/NCManageDatabase.swift b/iOSClient/Data/NCManageDatabase.swift index d2df51389..37f9aa4c7 100644 --- a/iOSClient/Data/NCManageDatabase.swift +++ b/iOSClient/Data/NCManageDatabase.swift @@ -149,6 +149,11 @@ class NCManageDatabase: NSObject { } } + if oldSchemaVersion < 222 && NCUtility.shared.SYSTEM_VERSION_LESS_THAN(version: "13") { + migration.deleteData(forType: tableMetadata.className()) + migration.deleteData(forType: tableDirectory.className()) + } + }, shouldCompactOnLaunch: { totalBytes, usedBytes in // totalBytes refers to the size of the file on disk in bytes (data + free space) @@ -245,6 +250,7 @@ class NCManageDatabase: NSObject { self.clearTable(tablePhotoLibrary.self, account: account) self.clearTable(tableShare.self, account: account) self.clearTable(tableTag.self, account: account) + self.clearTable(tableTip.self) self.clearTable(tableTrash.self, account: account) self.clearTable(tableUserStatus.self, account: account) self.clearTable(tableVideo.self, account: account) @@ -1547,6 +1553,37 @@ class NCManageDatabase: NSObject { } // MARK: - + // MARK: Table Tip + + @objc func tipExists(_ tipName: String) -> Bool { + + let realm = try! Realm() + + guard (realm.objects(tableTip.self).where { + $0.tipName == tipName + }.first) == nil else { + return true + } + + return false + } + + @objc func addTip(_ tipName: String) { + + let realm = try! Realm() + + do { + try realm.safeWrite { + let addObject = tableTip() + addObject.tipName = tipName + realm.add(addObject, update: .all) + } + } catch let error { + NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + } + } + + // MARK: - // MARK: Table Trash @objc func addTrash(account: String, items: [NCCommunicationTrash]) { @@ -1682,133 +1719,6 @@ class NCManageDatabase: NSObject { NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") } } - // MARK: - - // MARK: Table Video - - func addVideoTime(metadata: tableMetadata, time: CMTime?, durationTime: CMTime?) { - - if metadata.livePhoto { return } - let realm = try! Realm() - - do { - try realm.safeWrite { - if let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first { - - if let durationTime = durationTime { - result.duration = durationTime.convertScale(1000, method: .default).value - } - if let time = time { - result.time = time.convertScale(1000, method: .default).value - } - realm.add(result, update: .all) - - } else { - - let addObject = tableVideo() - - addObject.account = metadata.account - if let durationTime = durationTime { - addObject.duration = durationTime.convertScale(1000, method: .default).value - } - addObject.ocId = metadata.ocId - if let time = time { - addObject.time = time.convertScale(1000, method: .default).value - } - realm.add(addObject, update: .all) - } - } - } catch let error { - NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") - } - } - - func addVideoCodec(metadata: tableMetadata, codecNameVideo: String?, codecNameAudio: String?, codecAudioChannelLayout: String?, codecAudioLanguage: String?, codecSubtitleLanguage: String?, codecMaxCompatibility: Bool, codecQuality: String?) { - - let realm = try! Realm() - - do { - try realm.safeWrite { - if let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first { - if let codecNameVideo = codecNameVideo { result.codecNameVideo = codecNameVideo } - if let codecNameAudio = codecNameAudio { result.codecNameAudio = codecNameAudio } - if let codecAudioChannelLayout = codecAudioChannelLayout { result.codecAudioChannelLayout = codecAudioChannelLayout } - if let codecAudioLanguage = codecAudioLanguage { result.codecAudioLanguage = codecAudioLanguage } - if let codecSubtitleLanguage = codecSubtitleLanguage { result.codecSubtitleLanguage = codecSubtitleLanguage } - result.codecMaxCompatibility = codecMaxCompatibility - if let codecQuality = codecQuality { result.codecQuality = codecQuality } - realm.add(result, update: .all) - } else { - let addObject = tableVideo() - addObject.account = metadata.account - addObject.ocId = metadata.ocId - addObject.codecNameVideo = codecNameVideo - addObject.codecNameAudio = codecNameAudio - addObject.codecAudioChannelLayout = codecAudioChannelLayout - addObject.codecAudioLanguage = codecAudioLanguage - addObject.codecSubtitleLanguage = codecSubtitleLanguage - addObject.codecMaxCompatibility = codecMaxCompatibility - addObject.codecQuality = codecQuality - realm.add(addObject, update: .all) - } - } - } catch let error { - NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") - } - } - - func getVideo(metadata: tableMetadata?) -> tableVideo? { - guard let metadata = metadata else { return nil } - - let realm = try! Realm() - guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { - return nil - } - - return tableVideo.init(value: result) - } - - func getVideoDurationTime(metadata: tableMetadata?) -> CMTime? { - guard let metadata = metadata else { return nil } - - if metadata.livePhoto { return nil } - let realm = try! Realm() - - guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { - return nil - } - - if result.duration == 0 { return nil } - let duration = CMTimeMake(value: result.duration, timescale: 1000) - return duration - } - - func getVideoTime(metadata: tableMetadata) -> CMTime? { - - if metadata.livePhoto { return nil } - let realm = try! Realm() - - guard let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId).first else { - return nil - } - - if result.time == 0 { return nil } - let time = CMTimeMake(value: result.time, timescale: 1000) - return time - } - - func deleteVideo(metadata: tableMetadata) { - - let realm = try! Realm() - - do { - try realm.safeWrite { - let result = realm.objects(tableVideo.self).filter("account == %@ AND ocId == %@", metadata.account, metadata.ocId) - realm.delete(result) - } - } catch let error { - NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") - } - } } // MARK: - diff --git a/iOSClient/Diagnostics/NCCapabilitiesViewController.storyboard b/iOSClient/Diagnostics/NCCapabilitiesViewController.storyboard index e5846cd02..05f89c257 100644 --- a/iOSClient/Diagnostics/NCCapabilitiesViewController.storyboard +++ b/iOSClient/Diagnostics/NCCapabilitiesViewController.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="vTK-Er-kbZ"> +<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="vTK-Er-kbZ"> <device id="retina6_1" 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"/> @@ -21,7 +21,7 @@ <rect key="frame" x="0.0" y="88" width="414" height="774"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Sms-Ez-fLO" userLabel="View Capabilities"> - <rect key="frame" x="5" y="5" width="404" height="550"/> + <rect key="frame" x="5" y="5" width="404" height="600"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jdW-oZ-cH8" userLabel="FileSharing"> <rect key="frame" x="0.0" y="0.0" width="404" height="50"/> @@ -455,26 +455,68 @@ <constraint firstAttribute="trailing" secondItem="lVT-MG-7kN" secondAttribute="trailing" constant="5" id="zCZ-du-XT5"/> </constraints> </view> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="npG-0C-m3A" userLabel="Lock"> + <rect key="frame" x="0.0" y="550" width="404" height="50"/> + <subviews> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="lock" translatesAutoresizingMaskIntoConstraints="NO" id="eAC-Li-gKO"> + <rect key="frame" x="0.0" y="10" width="30" height="30"/> + <constraints> + <constraint firstAttribute="height" constant="30" id="JsP-Q6-6yG"/> + <constraint firstAttribute="width" constant="30" id="WUk-lh-3Vb"/> + </constraints> + </imageView> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Lock file" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SCj-6t-sXN"> + <rect key="frame" x="40" y="16" width="57.5" height="18"/> + <fontDescription key="fontDescription" type="system" pointSize="15"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Available" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="db5-st-O6M"> + <rect key="frame" x="279" y="12.5" width="120" height="25"/> + <color key="backgroundColor" systemColor="systemGray4Color"/> + <constraints> + <constraint firstAttribute="width" constant="120" id="Vfm-Rm-l7N"/> + <constraint firstAttribute="height" constant="25" id="oam-XV-KqH"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="12"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstItem="db5-st-O6M" firstAttribute="centerY" secondItem="npG-0C-m3A" secondAttribute="centerY" id="0X7-Jw-OjP"/> + <constraint firstItem="eAC-Li-gKO" firstAttribute="leading" secondItem="npG-0C-m3A" secondAttribute="leading" id="1yz-QU-lXY"/> + <constraint firstAttribute="height" constant="50" id="6GQ-cE-Nz3"/> + <constraint firstItem="SCj-6t-sXN" firstAttribute="centerY" secondItem="npG-0C-m3A" secondAttribute="centerY" id="Ocs-LT-OYb"/> + <constraint firstItem="eAC-Li-gKO" firstAttribute="centerY" secondItem="npG-0C-m3A" secondAttribute="centerY" id="SlK-BL-Nba"/> + <constraint firstAttribute="trailing" secondItem="db5-st-O6M" secondAttribute="trailing" constant="5" id="Upi-tI-m1R"/> + <constraint firstItem="SCj-6t-sXN" firstAttribute="leading" secondItem="eAC-Li-gKO" secondAttribute="trailing" constant="10" id="dgu-xW-E8T"/> + </constraints> + </view> </subviews> <color key="backgroundColor" systemColor="systemBackgroundColor"/> <constraints> <constraint firstItem="lGp-bh-Ysz" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="0qE-PV-O2f"/> <constraint firstAttribute="trailing" secondItem="8nf-zJ-Qas" secondAttribute="trailing" id="0vv-HQ-Qqx"/> - <constraint firstAttribute="height" constant="550" id="6nU-Cb-MzH"/> + <constraint firstAttribute="height" constant="600" id="6nU-Cb-MzH"/> <constraint firstItem="mSC-JU-xuk" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="9Nq-du-3ah"/> <constraint firstItem="S24-Wc-fps" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="Aad-cm-E1T"/> <constraint firstAttribute="trailing" secondItem="S24-Wc-fps" secondAttribute="trailing" id="Ajf-er-GBr"/> <constraint firstItem="ZNB-jF-9zg" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="GdE-lt-vZC"/> <constraint firstAttribute="trailing" secondItem="nVq-4i-FNy" secondAttribute="trailing" id="HSG-Ia-fYc"/> + <constraint firstItem="npG-0C-m3A" firstAttribute="top" secondItem="S24-Wc-fps" secondAttribute="bottom" constant="2" id="IQe-Xv-U7D"/> <constraint firstItem="S24-Wc-fps" firstAttribute="top" secondItem="2Pp-Kb-YMc" secondAttribute="bottom" constant="-2" id="ML8-GT-9hb"/> <constraint firstAttribute="trailing" secondItem="2Pp-Kb-YMc" secondAttribute="trailing" id="NG8-Nu-ic6"/> <constraint firstItem="LTt-2C-rPb" firstAttribute="top" secondItem="dhs-06-3RT" secondAttribute="bottom" id="Nm6-NH-AC9"/> + <constraint firstItem="npG-0C-m3A" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="QH1-s3-UQ7"/> <constraint firstItem="UPC-L1-VKj" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="QcN-sd-pHM"/> <constraint firstItem="LTt-2C-rPb" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="SUy-Mo-oAO"/> <constraint firstItem="jdW-oZ-cH8" firstAttribute="top" secondItem="Sms-Ez-fLO" secondAttribute="top" id="UBW-Mx-NTs"/> <constraint firstItem="8nf-zJ-Qas" firstAttribute="top" secondItem="UPC-L1-VKj" secondAttribute="bottom" id="UqS-Lx-6mL"/> <constraint firstItem="dhs-06-3RT" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="UwG-6b-pEk"/> <constraint firstItem="nVq-4i-FNy" firstAttribute="top" secondItem="mSC-JU-xuk" secondAttribute="bottom" id="VfU-sj-S9y"/> + <constraint firstAttribute="trailing" secondItem="npG-0C-m3A" secondAttribute="trailing" id="bLv-wr-d5v"/> <constraint firstAttribute="trailing" secondItem="lGp-bh-Ysz" secondAttribute="trailing" id="bUd-8w-D8k"/> <constraint firstItem="UPC-L1-VKj" firstAttribute="top" secondItem="lGp-bh-Ysz" secondAttribute="bottom" id="br5-nz-w7h"/> <constraint firstItem="8nf-zJ-Qas" firstAttribute="leading" secondItem="Sms-Ez-fLO" secondAttribute="leading" id="dsm-XA-dZD"/> @@ -495,7 +537,7 @@ </constraints> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ogC-ph-Xdr" userLabel="View Link"> - <rect key="frame" x="5" y="550" width="404" height="40"/> + <rect key="frame" x="5" y="620" width="404" height="40"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Home server" textAlignment="natural" lineBreakMode="characterWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PD5-8h-ZLm"> <rect key="frame" x="40" y="0.0" width="359" height="40"/> @@ -525,7 +567,7 @@ </constraints> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OO4-N7-9vp" userLabel="View JSON"> - <rect key="frame" x="0.0" y="600" width="414" height="40.5"/> + <rect key="frame" x="0.0" y="670" width="414" height="40.5"/> <subviews> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="RIO-6X-GG1"> <rect key="frame" x="5" y="5" width="404" height="30.5"/> @@ -545,8 +587,8 @@ </view> </subviews> <constraints> - <constraint firstItem="ogC-ph-Xdr" firstAttribute="top" secondItem="hTE-ys-qsF" secondAttribute="top" constant="550" id="9yP-Qs-EjJ"/> - <constraint firstItem="OO4-N7-9vp" firstAttribute="top" secondItem="hTE-ys-qsF" secondAttribute="top" constant="600" id="A9O-TK-Vz6" userLabel="View JSON.top = top + 550"/> + <constraint firstItem="ogC-ph-Xdr" firstAttribute="top" secondItem="hTE-ys-qsF" secondAttribute="top" constant="620" id="9yP-Qs-EjJ"/> + <constraint firstItem="OO4-N7-9vp" firstAttribute="top" secondItem="hTE-ys-qsF" secondAttribute="top" constant="670" id="A9O-TK-Vz6" userLabel="View JSON.top = top + 550"/> <constraint firstAttribute="trailing" secondItem="ogC-ph-Xdr" secondAttribute="trailing" constant="5" id="JLe-vC-Oyq"/> <constraint firstAttribute="bottom" secondItem="OO4-N7-9vp" secondAttribute="bottom" id="MpX-OZ-MDh"/> <constraint firstItem="OO4-N7-9vp" firstAttribute="width" secondItem="hTE-ys-qsF" secondAttribute="width" id="PWW-C3-Qcw"/> @@ -579,6 +621,7 @@ <outlet property="imageEndToEndEncryption" destination="S7m-5Z-ktw" id="0Pv-Yt-YJB"/> <outlet property="imageExternalSite" destination="JWO-C0-32L" id="JKi-n1-5IQ"/> <outlet property="imageFileSharing" destination="G9c-Nd-Ikl" id="Wha-2g-8o0"/> + <outlet property="imageLockFile" destination="eAC-Li-gKO" id="hHH-Pl-uOW"/> <outlet property="imageNotification" destination="cgb-3g-trc" id="fa5-99-76C"/> <outlet property="imageOnlyOffice" destination="xvv-h0-9bM" id="tw2-is-KHy"/> <outlet property="imageText" destination="iCB-2A-phO" id="uit-Ku-oOF"/> @@ -590,6 +633,7 @@ <outlet property="statusEndToEndEncryption" destination="M82-8U-M4Q" id="S9e-h3-GpF"/> <outlet property="statusExternalSite" destination="ivv-te-kaP" id="qzS-eo-Dq3"/> <outlet property="statusFileSharing" destination="SbT-rU-lJ8" id="zqA-0V-TLr"/> + <outlet property="statusLockFile" destination="db5-st-O6M" id="dRt-zm-Qd7"/> <outlet property="statusNotification" destination="WAg-Hw-sQS" id="T5C-Ch-11o"/> <outlet property="statusOnlyOffice" destination="ucV-YG-5ht" id="11e-La-p9K"/> <outlet property="statusText" destination="uiz-H8-p3D" id="wLb-D2-MNS"/> @@ -626,7 +670,7 @@ <image name="comments" width="425" height="425"/> <image name="delete" width="425" height="425"/> <image name="externalsites" width="425" height="425"/> - <image name="home" width="425" height="425"/> + <image name="home" width="120" height="120"/> <image name="lock" width="24" height="24"/> <image name="notification" width="512" height="512"/> <image name="onlyoffice" width="425" height="425"/> diff --git a/iOSClient/Diagnostics/NCCapabilitiesViewController.swift b/iOSClient/Diagnostics/NCCapabilitiesViewController.swift index c8d4ada34..6078ffa28 100644 --- a/iOSClient/Diagnostics/NCCapabilitiesViewController.swift +++ b/iOSClient/Diagnostics/NCCapabilitiesViewController.swift @@ -64,6 +64,9 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr @IBOutlet weak var homeImage: UIImageView! @IBOutlet weak var homeServer: UILabel! + @IBOutlet weak var imageLockFile: UIImageView! + @IBOutlet weak var statusLockFile: UILabel! + private let appDelegate = UIApplication.shared.delegate as! AppDelegate private var documentController: UIDocumentInteractionController? private var account: String = "" @@ -139,6 +142,11 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr statusComments.layer.borderColor = UIColor.gray.cgColor statusComments.layer.masksToBounds = true + statusLockFile.layer.cornerRadius = 12.5 + statusLockFile.layer.borderWidth = 0.5 + statusLockFile.layer.borderColor = UIColor.gray.cgColor + statusLockFile.layer.masksToBounds = true + imageFileSharing.image = UIImage(named: "share")!.image(color: NCBrandColor.shared.gray, size: 50) imageExternalSite.image = NCUtility.shared.loadImage(named: "network", color: NCBrandColor.shared.gray) imageEndToEndEncryption.image = NCUtility.shared.loadImage(named: "lock", color: NCBrandColor.shared.gray) @@ -150,6 +158,7 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr imageOnlyOffice.image = UIImage(named: "onlyoffice")!.image(color: NCBrandColor.shared.gray, size: 50) imageUserStatus.image = UIImage(named: "userStatusAway")!.image(color: NCBrandColor.shared.gray, size: 50) imageComments.image = UIImage(named: "comments")!.image(color: NCBrandColor.shared.gray, size: 50) + imageLockFile.image = UIImage(named: "lock")!.image(color: NCBrandColor.shared.gray, size: 50) guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return } self.account = activeAccount.account @@ -315,6 +324,13 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr statusComments.text = NSLocalizedString("_not_available_", comment: "") } + let hasLockCapability = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesLockVersion) >= 1 + if hasLockCapability { + statusLockFile.text = "✓ " + NSLocalizedString("_available_", comment: "") + } else { + statusLockFile.text = NSLocalizedString("_not_available_", comment: "") + } + print("end.") } } diff --git a/iOSClient/Extensions/DateFormatter+Extension.swift b/iOSClient/Extensions/DateFormatter+Extension.swift new file mode 100644 index 000000000..aa126ce78 --- /dev/null +++ b/iOSClient/Extensions/DateFormatter+Extension.swift @@ -0,0 +1,33 @@ +// +// DateFormatter+Extension.swift +// Nextcloud +// +// Created by Henrik Storch on 18.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 DateFormatter { + static let shareExpDate: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.formatterBehavior = .behavior10_4 + dateFormatter.dateStyle = .medium + return dateFormatter + }() +} diff --git a/iOSClient/Extensions/String+Extensions.swift b/iOSClient/Extensions/String+Extensions.swift index 40bd244af..f30751ed7 100644 --- a/iOSClient/Extensions/String+Extensions.swift +++ b/iOSClient/Extensions/String+Extensions.swift @@ -26,6 +26,11 @@ import UIKit import CommonCrypto extension String { + + var alphanumeric: String { + return self.components(separatedBy: CharacterSet.alphanumerics.inverted).joined().lowercased() + } + public var uppercaseInitials: String? { let initials = self.components(separatedBy: .whitespaces) .reduce("", { @@ -49,7 +54,7 @@ extension String { //https://stackoverflow.com/a/32166735/9506784 let length = Int(CC_MD5_DIGEST_LENGTH) - let messageData = self.data(using: .utf8)! + let messageData = self.data(using: .utf8) ?? Data() var digestData = Data(count: length) _ = digestData.withUnsafeMutableBytes { digestBytes -> UInt8 in diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift new file mode 100644 index 000000000..90a55c09d --- /dev/null +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -0,0 +1,91 @@ +// +// UIAlertController+Extension.swift +// Nextcloud +// +// Created by Henrik Storch on 27.01.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +extension UIAlertController { + /// Creates a alert controller with a textfield, asking to create a new folder + /// - Parameters: + /// - serverUrl: Server url of the location where the folder should be created + /// - urlBase: UrlBase object + /// - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter` + /// - Returns: The presentable alert controller + static func createFolder(serverUrl: String, urlBase: NCUserBaseUrl, completion: ((_ errorCode: Int, _ errorDescription: String) -> Void)? = nil) -> UIAlertController { + let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: nil, preferredStyle: .alert) + + let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in + guard let fileNameFolder = alertController.textFields?.first?.text else { return } + NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account, urlBase: urlBase.urlBase, overwrite: false) { errorCode, errorDescription in + if let completion = completion { + completion(errorCode, errorDescription) + } else if errorCode != 0 { + NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } // else: successful, no action + } + }) + + // text field is initially empty, no action + okAction.isEnabled = false + let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) + + alertController.addTextField { textField in + textField.autocapitalizationType = .words + } + + // only allow saving if folder name exists + NotificationCenter.default.addObserver( + forName: UITextField.textDidChangeNotification, + object: alertController.textFields?.first, + queue: .main) { _ in + guard let text = alertController.textFields?.first?.text, + let folderName = CCUtility.removeForbiddenCharactersServer(text)?.trimmingCharacters(in: .whitespaces) else { return } + okAction.isEnabled = !folderName.isEmpty && folderName != "." && folderName != ".." + } + + alertController.addAction(cancelAction) + alertController.addAction(okAction) + return alertController + } + + static func withTextField(titleKey: String, textFieldConfiguration: ((UITextField) -> Void)?, completion: @escaping (String?) -> Void) -> UIAlertController { + let alertController = UIAlertController(title: NSLocalizedString(titleKey, comment: ""), message: "", preferredStyle: .alert) + alertController.addTextField { textField in + textFieldConfiguration?(textField) + } + alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .default) { _ in }) + let okAction = UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default) { _ in + completion(alertController.textFields?.first?.text) + } + + alertController.addAction(okAction) + return alertController + } + + static func password(titleKey: String, completion: @escaping (String?) -> Void) -> UIAlertController { + return .withTextField(titleKey: titleKey, textFieldConfiguration: { textField in + textField.isSecureTextEntry = true + textField.placeholder = NSLocalizedString("_password_", comment: "") + }, completion: completion) + + } +} diff --git a/iOSClient/Extensions/UIApplication+Orientation.swift b/iOSClient/Extensions/UIApplication+Orientation.swift index d49c72e88..aa3f1528b 100644 --- a/iOSClient/Extensions/UIApplication+Orientation.swift +++ b/iOSClient/Extensions/UIApplication+Orientation.swift @@ -20,7 +20,7 @@ // 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 extension UIApplication { // indicates if current device is in landscape orientation diff --git a/iOSClient/Extensions/UIDevice+Extensions.swift b/iOSClient/Extensions/UIDevice+Extensions.swift new file mode 100644 index 000000000..07305585e --- /dev/null +++ b/iOSClient/Extensions/UIDevice+Extensions.swift @@ -0,0 +1,37 @@ +// +// UIDevice+Extensions.swift +// Nextcloud +// +// Created by Federico Malagoni on 23/02/22. +// Copyright © 2022 Federico Malagoni. All rights reserved. +// +// Author Federico Malagoni <federico.malagoni@astrairidium.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 UIDevice { + + var hasNotch: Bool { + if #available(iOS 11.0, *) { + if UIApplication.shared.windows.isEmpty { return false } + let top = UIApplication.shared.windows[0].safeAreaInsets.top + return top > 20 + } else { + // Fallback on earlier versions + return false + } + } +} diff --git a/iOSClient/Extensions/UIFont+Extension.swift b/iOSClient/Extensions/UIFont+Extension.swift new file mode 100644 index 000000000..42e5b9529 --- /dev/null +++ b/iOSClient/Extensions/UIFont+Extension.swift @@ -0,0 +1,31 @@ +// +// UIFont+Extensions.swift +// Nextcloud +// +// Created by Federico Malagoni on 23/02/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 UIKit + +extension UIFont { + + static func incosolataMedium(size: CGFloat) -> UIFont { + return UIFont(name: "Inconsolata-Medium", size: size)! + } +} diff --git a/iOSClient/Extensions/UIToolbar+Extension.swift b/iOSClient/Extensions/UIToolbar+Extension.swift new file mode 100644 index 000000000..983c4d7b3 --- /dev/null +++ b/iOSClient/Extensions/UIToolbar+Extension.swift @@ -0,0 +1,77 @@ +// +// UIToolbar+Extension.swift +// Nextcloud +// +// Created by Henrik Storch on 18.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +extension UIToolbar { + static func toolbar(onClear: (() -> Void)?, completion: @escaping () -> Void) -> UIToolbar { + let toolbar = UIToolbar() + toolbar.sizeToFit() + var buttons: [UIBarButtonItem] = [] + + if let onClear = onClear { + let clearButton = UIBarButtonItem(title: NSLocalizedString("_clear_", comment: ""), style: .plain) { + onClear() + } + buttons.append(clearButton) + } + buttons.append(UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)) + let doneButton = UIBarButtonItem(title: NSLocalizedString("_done_", comment: ""), style: .done) { + completion() + } + buttons.append(doneButton) + toolbar.setItems(buttons, animated: false) + return toolbar + } + + // by default inputAccessoryView does not respect safeArea + var wrappedSafeAreaContainer: UIView { + let view = InputBarWrapper() + view.addSubview(self) + self.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + self.topAnchor.constraint(equalTo: view.topAnchor), + self.leftAnchor.constraint(equalTo: view.leftAnchor), + self.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + self.rightAnchor.constraint(equalTo: view.rightAnchor) + ]) + return view + } +} + +// https://stackoverflow.com/a/67985180/9506784 +class InputBarWrapper: UIView { + + var desiredHeight: CGFloat = 0 { + didSet { invalidateIntrinsicContentSize() } + } + + override var intrinsicContentSize: CGSize { CGSize(width: 0, height: desiredHeight) } + + required init?(coder aDecoder: NSCoder) { fatalError() } + + override init(frame: CGRect) { + super.init(frame: frame) + autoresizingMask = .flexibleHeight + } +} diff --git a/iOSClient/Font/Inconsolata/Inconsolata-Black.ttf b/iOSClient/Font/Inconsolata/Inconsolata-Black.ttf Binary files differnew file mode 100644 index 000000000..45f991721 --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-Black.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-Bold.ttf b/iOSClient/Font/Inconsolata/Inconsolata-Bold.ttf Binary files differnew file mode 100644 index 000000000..9f9272588 --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-Bold.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-ExtraBold.ttf b/iOSClient/Font/Inconsolata/Inconsolata-ExtraBold.ttf Binary files differnew file mode 100644 index 000000000..ceefce0ee --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-ExtraBold.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-ExtraLight.ttf b/iOSClient/Font/Inconsolata/Inconsolata-ExtraLight.ttf Binary files differnew file mode 100644 index 000000000..8a4abb844 --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-ExtraLight.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-Light.ttf b/iOSClient/Font/Inconsolata/Inconsolata-Light.ttf Binary files differnew file mode 100644 index 000000000..2ce184299 --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-Light.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-Medium.ttf b/iOSClient/Font/Inconsolata/Inconsolata-Medium.ttf Binary files differnew file mode 100644 index 000000000..364328355 --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-Medium.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-Regular.ttf b/iOSClient/Font/Inconsolata/Inconsolata-Regular.ttf Binary files differnew file mode 100644 index 000000000..457d262cf --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-Regular.ttf diff --git a/iOSClient/Font/Inconsolata/Inconsolata-SemiBold.ttf b/iOSClient/Font/Inconsolata/Inconsolata-SemiBold.ttf Binary files differnew file mode 100644 index 000000000..f6a0d18fa --- /dev/null +++ b/iOSClient/Font/Inconsolata/Inconsolata-SemiBold.ttf diff --git a/iOSClient/Images.xcassets/pdf-vertical.imageset/Contents.json b/iOSClient/Images.xcassets/captions.bubble.imageset/Contents.json index 580932404..f17a1e2e0 100644 --- a/iOSClient/Images.xcassets/pdf-vertical.imageset/Contents.json +++ b/iOSClient/Images.xcassets/captions.bubble.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "swap-vertical-variant.svg", + "filename" : "subtitles-outline.svg", "idiom" : "universal" } ], diff --git a/iOSClient/Images.xcassets/pdf-horizontal.imageset/swap-horizontal-variant.svg b/iOSClient/Images.xcassets/captions.bubble.imageset/subtitles-outline.svg index b77ae3826..69a07d483 100644 --- a/iOSClient/Images.xcassets/pdf-horizontal.imageset/swap-horizontal-variant.svg +++ b/iOSClient/Images.xcassets/captions.bubble.imageset/subtitles-outline.svg @@ -1 +1 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M4,6L8,10V7H16A2,2 0 0,1 18,9A2,2 0 0,1 16,11H8A4,4 0 0,0 4,15A4,4 0 0,0 8,19H16V22L20,18L16,14V17H8A2,2 0 0,1 6,15A2,2 0 0,1 8,13H16A4,4 0 0,0 20,9A4,4 0 0,0 16,5H8V2L4,6Z" /></svg>
\ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20,4A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4H20M20,18V6H4V18H20M6,10H8V12H6V10M6,14H14V16H6V14M16,14H18V16H16V14M10,10H18V12H10V10Z" /></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/circle.fill.imageset/Contents.json b/iOSClient/Images.xcassets/circle_fill.imageset/Contents.json index b73baca81..b73baca81 100644 --- a/iOSClient/Images.xcassets/circle.fill.imageset/Contents.json +++ b/iOSClient/Images.xcassets/circle_fill.imageset/Contents.json diff --git a/iOSClient/Images.xcassets/circle.fill.imageset/circle.pdf b/iOSClient/Images.xcassets/circle_fill.imageset/circle.pdf Binary files differindex 4be68b831..4be68b831 100644 --- a/iOSClient/Images.xcassets/circle.fill.imageset/circle.pdf +++ b/iOSClient/Images.xcassets/circle_fill.imageset/circle.pdf diff --git a/iOSClient/Images.xcassets/closeVideo.imageset/Contents.json b/iOSClient/Images.xcassets/closeVideo.imageset/Contents.json deleted file mode 100644 index 27c37246d..000000000 --- a/iOSClient/Images.xcassets/closeVideo.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "closeVideo.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/iOSClient/Images.xcassets/closeVideo.imageset/closeVideo.pdf b/iOSClient/Images.xcassets/closeVideo.imageset/closeVideo.pdf Binary files differdeleted file mode 100644 index dd2befdd9..000000000 --- a/iOSClient/Images.xcassets/closeVideo.imageset/closeVideo.pdf +++ /dev/null diff --git a/iOSClient/Images.xcassets/edit.imageset/Contents.json b/iOSClient/Images.xcassets/edit.imageset/Contents.json deleted file mode 100644 index 8c2130e77..000000000 --- a/iOSClient/Images.xcassets/edit.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "edit.pdf" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - }, - "properties" : { - "preserves-vector-representation" : true - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/edit.imageset/edit.pdf b/iOSClient/Images.xcassets/edit.imageset/edit.pdf Binary files differdeleted file mode 100644 index fd83883ab..000000000 --- a/iOSClient/Images.xcassets/edit.imageset/edit.pdf +++ /dev/null diff --git a/iOSClient/Images.xcassets/userStatusOnline.imageset/Contents.json b/iOSClient/Images.xcassets/eye.imageset/Contents.json index 9f25229ad..704cf79b5 100644 --- a/iOSClient/Images.xcassets/userStatusOnline.imageset/Contents.json +++ b/iOSClient/Images.xcassets/eye.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "online.pdf", + "filename" : "eye.svg", "idiom" : "universal" } ], diff --git a/iOSClient/Images.xcassets/eye.imageset/eye.svg b/iOSClient/Images.xcassets/eye.imageset/eye.svg new file mode 100644 index 000000000..b1459db41 --- /dev/null +++ b/iOSClient/Images.xcassets/eye.imageset/eye.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 15.06"><path d="M12.004 15.06q1.774 0 3.351-.426 1.576-.425 2.911-1.124 1.334-.698 2.393-1.541 1.058-.843 1.804-1.69.747-.848 1.142-1.572Q24 7.982 24 7.535q0-.448-.395-1.173-.395-.724-1.142-1.572-.746-.847-1.804-1.69-1.059-.843-2.398-1.541Q16.922.861 15.346.435 13.769.009 12.004.009q-1.747 0-3.315.426-1.567.426-2.906 1.124-1.34.698-2.406 1.541-1.067.843-1.823 1.69Q.799 5.638.4 6.362 0 7.087 0 7.535q0 .447.4 1.172.399.724 1.154 1.572.756.847 1.818 1.69 1.063.843 2.402 1.541 1.339.699 2.907 1.124 1.567.426 3.323.426Zm0-1.387q-1.449 0-2.783-.36-1.335-.36-2.503-.953-1.168-.593-2.116-1.295-.949-.703-1.638-1.392-.69-.689-1.058-1.26-.369-.571-.369-.878 0-.264.369-.808.368-.545 1.058-1.243.689-.698 1.638-1.409.948-.712 2.116-1.322 1.168-.61 2.503-.984 1.334-.373 2.783-.373 1.441 0 2.771.373 1.33.374 2.498.984 1.168.61 2.121 1.322.953.711 1.638 1.409.685.698 1.058 1.243.373.544.373.808 0 .307-.373.878t-1.058 1.26q-.685.689-1.638 1.392-.953.702-2.121 1.295-1.168.593-2.498.953-1.33.36-2.771.36Zm0-1.221q1.019 0 1.915-.386.895-.387 1.572-1.067.676-.681 1.058-1.572t.382-1.892q0-1.028-.382-1.924-.382-.895-1.058-1.567-.677-.672-1.572-1.049-.896-.378-1.915-.378-1.036 0-1.932.378-.895.377-1.571 1.049-.677.672-1.059 1.567-.382.896-.382 1.924.009 1.001.391 1.892t1.058 1.572q.676.68 1.568 1.067.891.386 1.927.386Zm0-3.284q-.685 0-1.168-.483-.483-.483-.483-1.15 0-.677.483-1.155.483-.479 1.168-.479.677 0 1.16.479.483.478.483 1.155 0 .667-.483 1.15-.483.483-1.16.483Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/lock.open.imageset/Contents.json b/iOSClient/Images.xcassets/lock_open.imageset/Contents.json index d6fb0c7a0..d6fb0c7a0 100644 --- a/iOSClient/Images.xcassets/lock.open.imageset/Contents.json +++ b/iOSClient/Images.xcassets/lock_open.imageset/Contents.json diff --git a/iOSClient/Images.xcassets/lock.open.imageset/lock-open-variant-outline.svg b/iOSClient/Images.xcassets/lock_open.imageset/lock-open-variant-outline.svg index d1e4c56ff..d1e4c56ff 100644 --- a/iOSClient/Images.xcassets/lock.open.imageset/lock-open-variant-outline.svg +++ b/iOSClient/Images.xcassets/lock_open.imageset/lock-open-variant-outline.svg diff --git a/iOSClient/Images.xcassets/pdf-horizontal.imageset/Contents.json b/iOSClient/Images.xcassets/moreLock.imageset/Contents.json index 9a580cc28..194258945 100644 --- a/iOSClient/Images.xcassets/pdf-horizontal.imageset/Contents.json +++ b/iOSClient/Images.xcassets/moreLock.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "swap-horizontal-variant.svg", + "filename" : "menu-locked-filled.svg", "idiom" : "universal" } ], diff --git a/iOSClient/Images.xcassets/moreLock.imageset/menu-locked-filled.svg b/iOSClient/Images.xcassets/moreLock.imageset/menu-locked-filled.svg new file mode 100644 index 000000000..19be22ed7 --- /dev/null +++ b/iOSClient/Images.xcassets/moreLock.imageset/menu-locked-filled.svg @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + width="24" + height="24" + viewBox="0 0 24 24" + id="svg4" + sodipodi:docname="locked-filled.svg" + inkscape:version="1.1.2 (1:1.1+202202050950+0a00cf5339)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs8"> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath839"> + <path + d="M 0,0 H 1770.8333 V 1770.8333 H 0 Z" + clip-rule="evenodd" + id="path837" /> + </clipPath> + <clipPath + clipPathUnits="userSpaceOnUse" + id="clipPath839-3"> + <path + d="M 0,0 H 1770.8333 V 1770.8333 H 0 Z" + clip-rule="evenodd" + id="path837-6" /> + </clipPath> + </defs> + <sodipodi:namedview + id="namedview6" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="17" + inkscape:cx="9.7941176" + inkscape:cy="9.4117647" + inkscape:window-width="1920" + inkscape:window-height="1046" + inkscape:window-x="0" + inkscape:window-y="34" + inkscape:window-maximized="1" + inkscape:current-layer="svg4" /> + <path + d="m 15.75,6.375 c 0.690625,0 1.25,0.559375 1.25,1.25 v 6.25 c 0,0.690625 -0.559375,1.25 -1.25,1.25 H 8.25 C 7.55625,15.125 7,14.5625 7,13.875 V 7.625 C 7,6.93125 7.55625,6.375 8.25,6.375 H 8.875 V 5.125 C 8.875,3.399375 10.274375,2 12,2 c 1.725625,0 3.125,1.399375 3.125,3.125 v 1.25 H 15.75 M 12,3.25 c -1.035625,0 -1.875,0.839375 -1.875,1.875 v 1.25 h 3.75 V 5.125 C 13.875,4.089375 13.035625,3.25 12,3.25 Z" + id="path2" + style="stroke-width:0.625" + sodipodi:nodetypes="cssssssscsssccssccss" /> + <g + id="g833" + transform="matrix(0.01355297,0,0,0.01355297,-8.4406855e-6,6.9999789)"> + <g + id="g835" + clip-path="url(#clipPath839-3)"> + <g + id="g841" + transform="scale(73.784721)"> + <path + d="m 16,12 c 0,-0.0655 0.0032,-0.130847 0.0096,-0.196034 0.0064,-0.06519 0.01602,-0.129902 0.0288,-0.194147 0.01278,-0.06424 0.02868,-0.127705 0.04769,-0.190387 0.01901,-0.06268 0.04106,-0.124281 0.06612,-0.184798 0.02507,-0.06052 0.05304,-0.119658 0.08392,-0.177426 0.03088,-0.05777 0.06451,-0.113885 0.100903,-0.168348 0.03639,-0.05446 0.07536,-0.107011 0.116917,-0.157645 0.04155,-0.05063 0.08549,-0.09911 0.131809,-0.145428 0.04632,-0.04632 0.09479,-0.09025 0.145426,-0.131807 0.05063,-0.04155 0.103183,-0.08053 0.157646,-0.116919 0.05446,-0.03639 0.11058,-0.07003 0.168348,-0.100903 0.05777,-0.03088 0.116909,-0.05885 0.177425,-0.08392 0.06052,-0.02507 0.122118,-0.04711 0.1848,-0.06612 0.06268,-0.01902 0.126144,-0.03491 0.190387,-0.04769 0.06425,-0.01278 0.12896,-0.02238 0.194148,-0.0288 C 17.869154,10.003211 17.9345,10.000001 18,10 c 0.0655,10e-7 0.130848,0.0032 0.196033,0.0096 0.06519,0.0064 0.129904,0.01602 0.194148,0.0288 0.06425,0.01278 0.127708,0.02868 0.190389,0.04769 0.06268,0.01901 0.124281,0.04105 0.184796,0.06612 0.06052,0.02507 0.119659,0.05304 0.177427,0.08392 0.05777,0.03088 0.113882,0.06451 0.168346,0.100903 0.05446,0.03639 0.107016,0.07536 0.15765,0.116919 0.05063,0.04155 0.09911,0.08549 0.145424,0.131807 0.04632,0.04632 0.09025,0.09479 0.131806,0.145428 0.04155,0.05063 0.08053,0.103181 0.116916,0.157644 0.03639,0.05446 0.07003,0.110579 0.100905,0.168348 0.03088,0.05777 0.05885,0.11691 0.08392,0.177425 0.02507,0.06052 0.04711,0.122116 0.06612,0.184798 0.01901,0.06268 0.03491,0.126145 0.04769,0.190389 0.01278,0.06424 0.02238,0.12896 0.0288,0.194148 0.0064,0.06519 0.0096,0.130532 0.0096,0.196034 2e-6,0.0655 -0.0032,0.130848 -0.0096,0.196034 -0.0064,0.06519 -0.01602,0.129903 -0.0288,0.194146 -0.01278,0.06424 -0.02867,0.127707 -0.04769,0.190389 -0.01901,0.06268 -0.04105,0.12428 -0.06612,0.184796 -0.02506,0.06052 -0.05304,0.119658 -0.08392,0.177427 -0.03088,0.05777 -0.06451,0.113884 -0.100903,0.168346 -0.03639,0.05446 -0.07536,0.107013 -0.116918,0.157647 -0.04155,0.05063 -0.08549,0.09911 -0.131806,0.145427 -0.04632,0.04632 -0.09479,0.09025 -0.145426,0.131808 -0.05063,0.04155 -0.103183,0.08053 -0.157648,0.116918 -0.05446,0.03639 -0.110578,0.07003 -0.168346,0.100905 -0.05777,0.03088 -0.116909,0.05885 -0.177427,0.08391 -0.06051,0.02507 -0.122115,0.04711 -0.184796,0.06612 -0.06268,0.01902 -0.126144,0.03491 -0.190389,0.04769 -0.06424,0.01278 -0.12896,0.02238 -0.194148,0.0288 -0.06518,0.0064 -0.130529,0.0096 -0.196033,0.0096 -0.0655,0 -0.130846,-0.0032 -0.196033,-0.0096 -0.06519,-0.0064 -0.129904,-0.01602 -0.194149,-0.0288 -0.06424,-0.01278 -0.127707,-0.02867 -0.19039,-0.04769 -0.06268,-0.01901 -0.124281,-0.04105 -0.184797,-0.06612 -0.06052,-0.02507 -0.119658,-0.05304 -0.177426,-0.08392 -0.05777,-0.03088 -0.113886,-0.06451 -0.168348,-0.100905 -0.05446,-0.03639 -0.107012,-0.07536 -0.157646,-0.116917 -0.05063,-0.04155 -0.09911,-0.08549 -0.145424,-0.131808 -0.04632,-0.04632 -0.09025,-0.09479 -0.131809,-0.145427 -0.04155,-0.05063 -0.08053,-0.103183 -0.116919,-0.157647 -0.03639,-0.05446 -0.07003,-0.110578 -0.100903,-0.168346 -0.03088,-0.05777 -0.05885,-0.11691 -0.08392,-0.177427 -0.02507,-0.06052 -0.04711,-0.122115 -0.06612,-0.184797 -0.01901,-0.06268 -0.03491,-0.126145 -0.04769,-0.190389 -0.01278,-0.06424 -0.02238,-0.128958 -0.0288,-0.194146 C 16.00321,12.130848 16,12.065503 16,12 m -6,0 c 10e-7,-0.0655 0.0032,-0.130847 0.0096,-0.196034 0.0064,-0.06519 0.01602,-0.129902 0.0288,-0.194147 0.01278,-0.06424 0.02868,-0.127705 0.04769,-0.190387 0.01901,-0.06268 0.04105,-0.124281 0.06612,-0.184798 0.02507,-0.06052 0.05304,-0.119658 0.08392,-0.177426 0.03088,-0.05777 0.06451,-0.113885 0.100904,-0.168348 0.03639,-0.05446 0.07536,-0.107011 0.116919,-0.157645 0.04156,-0.05063 0.08549,-0.09911 0.131807,-0.145428 0.04632,-0.04632 0.09479,-0.09025 0.145427,-0.131807 0.05063,-0.04155 0.103182,-0.08053 0.157646,-0.116919 0.05446,-0.03639 0.110579,-0.07003 0.168348,-0.100903 0.05777,-0.03088 0.11691,-0.05885 0.177426,-0.08392 0.06052,-0.02507 0.122116,-0.04711 0.184798,-0.06612 0.06268,-0.01902 0.126144,-0.03491 0.190387,-0.04769 0.06424,-0.01278 0.12896,-0.02238 0.194147,-0.0288 0.06519,-0.0064 0.130532,-0.0096 0.196034,-0.0096 0.0655,10e-7 0.130848,0.0032 0.196034,0.0096 0.06519,0.0064 0.129903,0.01602 0.194146,0.0288 0.06424,0.01278 0.127707,0.02868 0.190389,0.04769 0.06268,0.01901 0.12428,0.04105 0.184796,0.06612 0.06052,0.02507 0.119658,0.05304 0.177427,0.08392 0.05777,0.03088 0.113884,0.06451 0.168346,0.100903 0.05446,0.03639 0.107013,0.07536 0.157647,0.116919 0.05063,0.04155 0.09911,0.08549 0.145427,0.131807 0.04632,0.04632 0.09025,0.09479 0.131808,0.145428 0.04155,0.05063 0.08053,0.103181 0.116918,0.157644 0.03639,0.05446 0.07003,0.110579 0.100905,0.168348 0.03088,0.05777 0.05885,0.11691 0.08391,0.177425 0.02507,0.06052 0.04711,0.122116 0.06612,0.184798 0.01902,0.06268 0.03491,0.126145 0.04769,0.190389 0.01278,0.06424 0.02238,0.12896 0.0288,0.194148 0.0064,0.06519 0.0096,0.130532 0.0096,0.196034 -10e-7,0.0655 -0.0032,0.130848 -0.0096,0.196034 -0.0064,0.06519 -0.01602,0.129903 -0.0288,0.194146 -0.01278,0.06424 -0.02867,0.127707 -0.04769,0.190389 -0.01901,0.06268 -0.04105,0.12428 -0.06612,0.184796 -0.02507,0.06052 -0.05304,0.119658 -0.08391,0.177427 -0.03088,0.05777 -0.06451,0.113884 -0.100905,0.168346 -0.03639,0.05446 -0.07536,0.107013 -0.116917,0.157647 -0.04156,0.05063 -0.08549,0.09911 -0.131808,0.145427 -0.04632,0.04632 -0.09479,0.09025 -0.145427,0.131808 -0.05063,0.04155 -0.103183,0.08053 -0.157647,0.116918 -0.05446,0.03639 -0.110577,0.07003 -0.168346,0.100905 -0.05777,0.03088 -0.116911,0.05885 -0.177427,0.08391 -0.06052,0.02507 -0.122115,0.04711 -0.184797,0.06612 -0.06268,0.01902 -0.126144,0.03491 -0.190388,0.04769 -0.06424,0.01278 -0.128958,0.02238 -0.194146,0.0288 -0.06519,0.0064 -0.130531,0.0096 -0.196034,0.0096 -0.0655,0 -0.130847,-0.0032 -0.196034,-0.0096 -0.06519,-0.0064 -0.129903,-0.01602 -0.194148,-0.0288 -0.06424,-0.01278 -0.127707,-0.02867 -0.190389,-0.04769 -0.06268,-0.01901 -0.124281,-0.04105 -0.184798,-0.06612 -0.06052,-0.02507 -0.119658,-0.05304 -0.177425,-0.08392 -0.05777,-0.03088 -0.113885,-0.06451 -0.168348,-0.100905 -0.05446,-0.03639 -0.107011,-0.07536 -0.157644,-0.116917 -0.05063,-0.04155 -0.09911,-0.08549 -0.145428,-0.131808 -0.04632,-0.04632 -0.09025,-0.09479 -0.131807,-0.145427 -0.04156,-0.05063 -0.08053,-0.103183 -0.11692,-0.157647 -0.03639,-0.05446 -0.07002,-0.110578 -0.100902,-0.168346 -0.03088,-0.05777 -0.05885,-0.11691 -0.08392,-0.177427 -0.02507,-0.06052 -0.04711,-0.122115 -0.06612,-0.184797 -0.01901,-0.06268 -0.03491,-0.126145 -0.04769,-0.190389 -0.01278,-0.06424 -0.02238,-0.128958 -0.0288,-0.194146 C 10.003211,12.130848 10.000001,12.065503 10,12 m -6,0 c 0,-0.0655 0.00321,-0.130847 0.00963,-0.196034 0.00642,-0.06519 0.01602,-0.129902 0.028799,-0.194147 0.012779,-0.06424 0.028676,-0.127705 0.04769,-0.190387 0.019014,-0.06268 0.041055,-0.124281 0.066121,-0.184798 0.025067,-0.06052 0.053039,-0.119658 0.083917,-0.177426 0.030878,-0.05777 0.064512,-0.113885 0.1009026,-0.168348 0.036392,-0.05446 0.075365,-0.107011 0.116919,-0.157645 0.041554,-0.05063 0.08549,-0.09911 0.1318068,-0.145428 0.046318,-0.04632 0.094793,-0.09025 0.1454268,-0.131807 0.050634,-0.04155 0.1031833,-0.08053 0.1576462,-0.116919 0.054463,-0.03639 0.110579,-0.07003 0.1683473,-0.100903 0.057768,-0.03088 0.1169105,-0.05885 0.1774268,-0.08392 0.060516,-0.02507 0.1221157,-0.04711 0.1847973,-0.06612 0.062682,-0.01902 0.1261444,-0.03491 0.1903882,-0.04769 0.064244,-0.01278 0.1289597,-0.02238 0.1941467,-0.0288 C 5.869153,10.003211 5.9344978,10.000001 6,10 c 0.065503,10e-7 0.1308475,0.0032 0.1960344,0.0096 0.065186,0.0064 0.1299014,0.01602 0.1941452,0.0288 0.064244,0.01278 0.127707,0.02868 0.1903887,0.04769 0.062682,0.01901 0.1242809,0.04105 0.1847973,0.06612 0.060517,0.02507 0.1196589,0.05304 0.1774268,0.08392 0.057768,0.03088 0.1138845,0.06451 0.1683474,0.100903 0.054464,0.03639 0.1070127,0.07536 0.1576466,0.116919 0.050634,0.04155 0.09911,0.08549 0.1454268,0.131807 0.046317,0.04632 0.090253,0.09479 0.1318073,0.145428 0.041555,0.05063 0.080527,0.103181 0.1169181,0.157644 0.036391,0.05446 0.070026,0.110579 0.100904,0.168348 0.030878,0.05777 0.058849,0.11691 0.083916,0.177425 0.025067,0.06052 0.047108,0.122116 0.066122,0.184798 0.019014,0.06268 0.034911,0.126145 0.04769,0.190389 0.012779,0.06424 0.022379,0.12896 0.028799,0.194148 C 7.9967899,11.869153 8,11.934498 8,12 c 0,0.0655 -0.00321,0.130848 -0.00963,0.196034 -0.00642,0.06519 -0.016021,0.129903 -0.0288,0.194146 -0.012779,0.06424 -0.028675,0.127707 -0.04769,0.190389 -0.019014,0.06268 -0.041055,0.12428 -0.066122,0.184796 -0.025066,0.06052 -0.053039,0.119658 -0.083917,0.177427 -0.030878,0.05777 -0.064512,0.113884 -0.1009035,0.168346 -0.036391,0.05446 -0.075364,0.107013 -0.1169181,0.157647 -0.041554,0.05063 -0.08549,0.09911 -0.1318068,0.145427 -0.046317,0.04632 -0.094792,0.09025 -0.1454272,0.131808 -0.050634,0.04155 -0.1031828,0.08053 -0.1576467,0.116918 -0.054463,0.03639 -0.1105785,0.07003 -0.1683464,0.100905 -0.057768,0.03088 -0.11691,0.05885 -0.1774268,0.08391 -0.060516,0.02507 -0.1221152,0.04711 -0.1847973,0.06612 -0.062682,0.01902 -0.1261449,0.03491 -0.1903887,0.04769 -0.064244,0.01278 -0.1289592,0.02238 -0.1941457,0.0288 C 6.1308475,13.996789 6.0655026,13.999999 6,14 5.934498,14 5.869153,13.9968 5.803966,13.9904 5.738779,13.984 5.6740627,13.97438 5.6098189,13.9616 5.5455749,13.94882 5.4821124,13.93293 5.4194303,13.91391 5.3567483,13.8949 5.2951498,13.87286 5.234633,13.84779 5.174117,13.82272 5.1149745,13.79475 5.0572062,13.76387 4.9994382,13.73299 4.9433227,13.69936 4.8888593,13.662965 4.8343963,13.626575 4.7818475,13.587605 4.7312136,13.546048 4.6805796,13.504498 4.6321036,13.460558 4.5857863,13.41424 4.5394693,13.36792 4.4955333,13.31945 4.4539795,13.268813 4.4124245,13.218183 4.3734525,13.16563 4.3370605,13.111166 4.3006695,13.056706 4.2670355,13.000588 4.2361574,12.94282 4.2052794,12.88505 4.1773074,12.82591 4.1522404,12.765393 4.1271734,12.704873 4.1051334,12.643278 4.0861194,12.580596 4.0671044,12.517916 4.0512074,12.454451 4.0384294,12.390207 4.02565,12.325937 4.0160508,12.261222 4.0096302,12.196034 4.0032101,12.130848 4,12.065503 4,12 Z" + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path843" /> + </g> + </g> + </g> +</svg> diff --git a/iOSClient/Images.xcassets/pdf-vertical.imageset/swap-vertical-variant.svg b/iOSClient/Images.xcassets/pdf-vertical.imageset/swap-vertical-variant.svg deleted file mode 100644 index 72050e85d..000000000 --- a/iOSClient/Images.xcassets/pdf-vertical.imageset/swap-vertical-variant.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M18,4L14,8H17V16A2,2 0 0,1 15,18A2,2 0 0,1 13,16V8A4,4 0 0,0 9,4A4,4 0 0,0 5,8V16H2L6,20L10,16H7V8A2,2 0 0,1 9,6A2,2 0 0,1 11,8V16A4,4 0 0,0 15,20A4,4 0 0,0 19,16V8H22L18,4Z" /></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorCancel.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorCancel.imageset/Contents.json deleted file mode 100644 index d979ab59b..000000000 --- a/iOSClient/Images.xcassets/photoEditorCancel.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorCancel.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorCancel@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorCancel@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel.png b/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel.png Binary files differdeleted file mode 100644 index 67b045d96..000000000 --- a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@2x.png b/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@2x.png Binary files differdeleted file mode 100644 index 9e586f136..000000000 --- a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@3x.png b/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@3x.png Binary files differdeleted file mode 100644 index 51e41a5ac..000000000 --- a/iOSClient/Images.xcassets/photoEditorCancel.imageset/photoEditorCancel@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorClear.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorClear.imageset/Contents.json deleted file mode 100644 index a25af1e47..000000000 --- a/iOSClient/Images.xcassets/photoEditorClear.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorClear.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorClear@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorClear@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear.png b/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear.png Binary files differdeleted file mode 100644 index cf1bdeec6..000000000 --- a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@2x.png b/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@2x.png Binary files differdeleted file mode 100644 index d79aa9abd..000000000 --- a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@3x.png b/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@3x.png Binary files differdeleted file mode 100644 index ac81fb574..000000000 --- a/iOSClient/Images.xcassets/photoEditorClear.imageset/photoEditorClear@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorCrop.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorCrop.imageset/Contents.json deleted file mode 100644 index debea08a8..000000000 --- a/iOSClient/Images.xcassets/photoEditorCrop.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorCrop.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorCrop@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorCrop@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop.png b/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop.png Binary files differdeleted file mode 100644 index 7e4f85742..000000000 --- a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@2x.png b/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@2x.png Binary files differdeleted file mode 100644 index ad9b4d0d2..000000000 --- a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@3x.png b/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@3x.png Binary files differdeleted file mode 100644 index a94ad5720..000000000 --- a/iOSClient/Images.xcassets/photoEditorCrop.imageset/photoEditorCrop@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDone.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorDone.imageset/Contents.json deleted file mode 100644 index 39d8a0da1..000000000 --- a/iOSClient/Images.xcassets/photoEditorDone.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorDone.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorDone@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorDone@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone.png b/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone.png Binary files differdeleted file mode 100644 index 4763dc297..000000000 --- a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@2x.png b/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@2x.png Binary files differdeleted file mode 100644 index 1ddc90b92..000000000 --- a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@3x.png b/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@3x.png Binary files differdeleted file mode 100644 index 2eaf2ba59..000000000 --- a/iOSClient/Images.xcassets/photoEditorDone.imageset/photoEditorDone@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDraw.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorDraw.imageset/Contents.json deleted file mode 100644 index f409ad8e8..000000000 --- a/iOSClient/Images.xcassets/photoEditorDraw.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorDraw.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorDraw@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorDraw@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw.png b/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw.png Binary files differdeleted file mode 100644 index 96fd9e393..000000000 --- a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@2x.png b/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@2x.png Binary files differdeleted file mode 100644 index cd8d000ec..000000000 --- a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@3x.png b/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@3x.png Binary files differdeleted file mode 100644 index aa8365791..000000000 --- a/iOSClient/Images.xcassets/photoEditorDraw.imageset/photoEditorDraw@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorText.imageset/Contents.json b/iOSClient/Images.xcassets/photoEditorText.imageset/Contents.json deleted file mode 100644 index 91c57f675..000000000 --- a/iOSClient/Images.xcassets/photoEditorText.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "photoEditorText.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorText@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "photoEditorText@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText.png b/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText.png Binary files differdeleted file mode 100644 index ba7bc7cea..000000000 --- a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@2x.png b/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@2x.png Binary files differdeleted file mode 100644 index 5c9271396..000000000 --- a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@3x.png b/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@3x.png Binary files differdeleted file mode 100644 index 40725d067..000000000 --- a/iOSClient/Images.xcassets/photoEditorText.imageset/photoEditorText@3x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/tabBarFavorites.imageset/Contents.json b/iOSClient/Images.xcassets/tabBarFavorites.imageset/Contents.json deleted file mode 100644 index cfafcd9de..000000000 --- a/iOSClient/Images.xcassets/tabBarFavorites.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "tabBarFavorites.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/iOSClient/Images.xcassets/tabBarFavorites.imageset/tabBarFavorites.pdf b/iOSClient/Images.xcassets/tabBarFavorites.imageset/tabBarFavorites.pdf Binary files differdeleted file mode 100644 index 25f0cd639..000000000 --- a/iOSClient/Images.xcassets/tabBarFavorites.imageset/tabBarFavorites.pdf +++ /dev/null diff --git a/iOSClient/Images.xcassets/tabBarMedia.imageset/Contents.json b/iOSClient/Images.xcassets/tabBarMedia.imageset/Contents.json deleted file mode 100644 index c42193f0f..000000000 --- a/iOSClient/Images.xcassets/tabBarMedia.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "tabBarMedia.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/iOSClient/Images.xcassets/tabBarMedia.imageset/tabBarMedia.pdf b/iOSClient/Images.xcassets/tabBarMedia.imageset/tabBarMedia.pdf Binary files differdeleted file mode 100644 index 515ca27a1..000000000 --- a/iOSClient/Images.xcassets/tabBarMedia.imageset/tabBarMedia.pdf +++ /dev/null diff --git a/iOSClient/Images.xcassets/userStatusOnline.imageset/online.pdf b/iOSClient/Images.xcassets/userStatusOnline.imageset/online.pdf Binary files differdeleted file mode 100644 index 672db3e6e..000000000 --- a/iOSClient/Images.xcassets/userStatusOnline.imageset/online.pdf +++ /dev/null diff --git a/iOSClient/Images.xcassets/visiblePassword.imageset/Contents.json b/iOSClient/Images.xcassets/visiblePassword.imageset/Contents.json deleted file mode 100644 index fb45aa9ac..000000000 --- a/iOSClient/Images.xcassets/visiblePassword.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "visiblePassword.png" - }, - { - "idiom" : "universal", - "filename" : "visiblePassword@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x", - "filename" : "visiblePassword@3x.png" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -}
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword.png b/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword.png Binary files differdeleted file mode 100644 index 2e56118f3..000000000 --- a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@2x.png b/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@2x.png Binary files differdeleted file mode 100644 index 3a191b07b..000000000 --- a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@2x.png +++ /dev/null diff --git a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@3x.png b/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@3x.png Binary files differdeleted file mode 100644 index 80246c39e..000000000 --- a/iOSClient/Images.xcassets/visiblePassword.imageset/visiblePassword@3x.png +++ /dev/null diff --git a/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift b/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift index fd431bd06..6c12e8346 100644 --- a/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift +++ b/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift @@ -93,7 +93,6 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate { if recording.state == .record { - startDate = Date() recording.stop() voiceRecordHUD.update(0.0) @@ -105,6 +104,7 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate { do { try recording.record() + startDate = Date() startStopLabel.text = NSLocalizedString("_voice_memo_stop_", comment: "") } catch { print(error) @@ -188,8 +188,8 @@ open class NCAudioRecorder: NSObject { // MARK: - Initializers - public init(to: String) { - url = URL(fileURLWithPath: NCAudioRecorder.directory).appendingPathComponent(to) + public init(to fileName: String) { + url = URL(fileURLWithPath: NCAudioRecorder.directory).appendingPathComponent(fileName) super.init() do { @@ -278,7 +278,6 @@ open class NCAudioRecorder: NSObject { } fileprivate func stopMetering() { - link?.invalidate() link = nil } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index 0ff8de7a5..0b8a7c674 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -44,7 +44,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS internal var metadataFolder: tableMetadata? internal var dataSource = NCDataSource() internal var richWorkspaceText: String? - internal var header: UIView? + internal var header: NCSectionHeaderMenu? internal var layoutForView: NCGlobal.layoutForViewType? @@ -615,7 +615,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS button.action(for: .touchUpInside) { _ in let accounts = NCManageDatabase.shared.getAllAccountOrderAlias() - if accounts.count > 0 { + if accounts.count > 0 && !NCBrandOptions.shared.disable_multiaccount && !NCBrandOptions.shared.disable_manage_account { if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest { @@ -741,6 +741,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS 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 @@ -751,6 +752,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) } 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 @@ -789,7 +791,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(objectId) else { return } - if namedButtonMore == NCGlobal.shared.buttonMoreMore { + if namedButtonMore == NCGlobal.shared.buttonMoreMore || namedButtonMore == NCGlobal.shared.buttonMoreLock { toggleMenu(metadata: metadata, imageIcon: image) } else if namedButtonMore == NCGlobal.shared.buttonMoreStop { NCNetworking.shared.cancelTransferMetadata(metadata) { } @@ -1267,8 +1269,10 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { 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 @@ -1388,6 +1392,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { 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 @@ -1396,6 +1401,11 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.progressView.progress = 0.0 } + var a11yValues: [String] = [] + if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { + a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) + } + if metadata.directory { if metadata.e2eEncrypted { @@ -1428,6 +1438,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // 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 @@ -1437,6 +1448,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // image Favorite if metadata.favorite { cell.imageFavorite.image = NCBrandColor.cacheImages.favorite + a11yValues.append(NSLocalizedString("_favorite_", comment: "")) } // Share image @@ -1455,6 +1467,9 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { 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) } @@ -1490,9 +1505,12 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { break } + cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "") + // Live Photo if metadata.livePhoto { cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto + a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) } // E2EE @@ -1514,12 +1532,14 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.selectMode(true) if selectOcId.contains(metadata.ocId) { cell.selected(true) + a11yValues.append(NSLocalizedString("_selected_", comment: "")) } else { cell.selected(false) } } else { cell.selectMode(false) } + cell.accessibilityValue = a11yValues.joined(separator: ", ") // Disable Share Button if appDelegate.disableSharesView { @@ -1559,9 +1579,16 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { 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 + } + + var a11yValues: [String] = [] + if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { + a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) } if metadata.directory { @@ -1597,6 +1624,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // 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 } @@ -1605,11 +1633,15 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // image Favorite if metadata.favorite { cell.imageFavorite.image = NCBrandColor.cacheImages.favorite + a11yValues.append(NSLocalizedString("_favorite_", comment: "")) } // 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) } @@ -1617,6 +1649,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // Live Photo if metadata.livePhoto { cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto + a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) } // Edit mode @@ -1624,12 +1657,14 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.selectMode(true) if selectOcId.contains(metadata.ocId) { cell.selected(true) + a11yValues.append(NSLocalizedString("_selected_", comment: "")) } else { cell.selected(false) } } else { cell.selectMode(false) } + cell.accessibilityValue = a11yValues.joined(separator: ", ") return cell } diff --git a/iOSClient/Main/Collection Common/NCGridCell.swift b/iOSClient/Main/Collection Common/NCGridCell.swift index e458e3cd2..9fcd361c8 100644 --- a/iOSClient/Main/Collection Common/NCGridCell.swift +++ b/iOSClient/Main/Collection Common/NCGridCell.swift @@ -72,6 +72,12 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto override func awakeFromNib() { super.awakeFromNib() + // use entire cell as accessibility element + accessibilityHint = nil + accessibilityLabel = nil + accessibilityValue = nil + isAccessibilityElement = true + imageItem.layer.cornerRadius = 6 imageItem.layer.masksToBounds = true @@ -99,6 +105,9 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto override func prepareForReuse() { super.prepareForReuse() imageItem.backgroundColor = nil + accessibilityHint = nil + accessibilityLabel = nil + accessibilityValue = nil } @IBAction func touchUpInsideMore(_ sender: Any) { @@ -113,9 +122,21 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto delegate?.longPressGridItem(with: objectId, gestureRecognizer: gestureRecognizer) } + fileprivate func setA11yActions() { + let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_" + + self.accessibilityCustomActions = [ + UIAccessibilityCustomAction( + name: NSLocalizedString(moreName, comment: ""), + target: self, + selector: #selector(touchUpInsideMore)) + ] + } + func setButtonMore(named: String, image: UIImage) { namedButtonMore = named buttonMore.setImage(image, for: .normal) + setA11yActions() } func hideButtonMore(_ status: Bool) { @@ -125,9 +146,11 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto func selectMode(_ status: Bool) { if status { imageSelect.isHidden = false + accessibilityCustomActions = nil } else { imageSelect.isHidden = true imageVisualEffect.isHidden = true + setA11yActions() } } diff --git a/iOSClient/Main/Collection Common/NCListCell.swift b/iOSClient/Main/Collection Common/NCListCell.swift index 9e0266f65..71e3bd43a 100755 --- a/iOSClient/Main/Collection Common/NCListCell.swift +++ b/iOSClient/Main/Collection Common/NCListCell.swift @@ -80,6 +80,12 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto imageItem.layer.cornerRadius = 6 imageItem.layer.masksToBounds = true + // use entire cell as accessibility element + accessibilityHint = nil + accessibilityLabel = nil + accessibilityValue = nil + isAccessibilityElement = true + progressView.tintColor = NCBrandColor.shared.brandElement progressView.transform = CGAffineTransform(scaleX: 1.0, y: 0.5) progressView.trackTintColor = .clear @@ -103,6 +109,9 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto override func prepareForReuse() { super.prepareForReuse() imageItem.backgroundColor = nil + accessibilityHint = nil + accessibilityLabel = nil + accessibilityValue = nil } @IBAction func touchUpInsideShare(_ sender: Any) { @@ -121,9 +130,25 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto delegate?.longPressListItem(with: objectId, gestureRecognizer: gestureRecognizer) } + fileprivate func setA11yActions() { + let moreName = namedButtonMore == NCGlobal.shared.buttonMoreStop ? "_cancel_" : "_more_" + self.accessibilityCustomActions = [ + UIAccessibilityCustomAction( + name: NSLocalizedString("_share_", comment: ""), + target: self, + selector: #selector(touchUpInsideShare)), + UIAccessibilityCustomAction( + name: NSLocalizedString(moreName, comment: ""), + target: self, + selector: #selector(touchUpInsideMore)) + ] + } + func setButtonMore(named: String, image: UIImage) { namedButtonMore = named imageMore.image = image + + setA11yActions() } func hideButtonMore(_ status: Bool) { @@ -140,10 +165,12 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto if status { imageItemLeftConstraint.constant = 45 imageSelect.isHidden = false + accessibilityCustomActions = nil } else { imageItemLeftConstraint.constant = 10 imageSelect.isHidden = true backgroundView = nil + setA11yActions() } } diff --git a/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift index 86aa18c8f..84b706e48 100644 --- a/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift +++ b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift @@ -95,6 +95,9 @@ extension NCSelectableNavigationView where Self: UIViewController { var selectedMetadatas: [tableMetadata] = [] var selectedMediaMetadatas: [tableMetadata] = [] var isAnyOffline = false + var isAnyFolder = false + var isAnyLocked = false + var canUnlock = true for ocId in selectOcId { guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue } @@ -102,6 +105,13 @@ extension NCSelectableNavigationView where Self: UIViewController { if [NCCommunicationCommon.typeClassFile.image.rawValue, NCCommunicationCommon.typeClassFile.video.rawValue].contains(metadata.classFile) { selectedMediaMetadatas.append(metadata) } + if metadata.directory { isAnyFolder = true } + if metadata.lock { + isAnyLocked = true + if metadata.lockOwner != appDelegate.userId { + canUnlock = false + } + } guard !isAnyOffline else { continue } if metadata.directory, @@ -114,6 +124,10 @@ extension NCSelectableNavigationView where Self: UIViewController { actions.append(.openInAction(selectedMetadatas: selectedMetadatas, viewController: self, completion: tapSelect)) + if !isAnyFolder, canUnlock, NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesLockVersion) >= 1 { + actions.append(.lockUnlockFiles(shouldLock: !isAnyLocked, metadatas: selectedMetadatas, completion: tapSelect)) + } + if !selectedMediaMetadatas.isEmpty { actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect)) } diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift index 686abeefa..adcd666d2 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift @@ -415,7 +415,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { metadataForUpload.assetLocalIdentifier = asset.localIdentifier metadataForUpload.session = self.session metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile - metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(asset: asset) metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload if livePhoto { diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift index 0816812c5..48cda3166 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift @@ -423,7 +423,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource { } else { - CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadataNewFile, notification: false) { metadataNew, fileNamePath in + CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadataNewFile) { metadataNew, fileNamePath in if metadataNew != nil { self.fileNamesPath[metadataNewFile.fileNameView] = fileNamePath! diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift index 6d90735b9..313215913 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift @@ -328,7 +328,9 @@ import XLForm if self.editorId == NCGlobal.shared.editorOnlyoffice { customUserAgent = NCUtility.shared.getCustomUserAgentOnlyOffice() - } + } else if editorId == NCGlobal.shared.editorText { + customUserAgent = NCUtility.shared.getCustomUserAgentNCText() + } // else: use default NCCommunication.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, customUserAgent: customUserAgent) { account, url, errorCode, errorMessage in @@ -396,7 +398,10 @@ import XLForm var customUserAgent: String? if self.editorId == NCGlobal.shared.editorOnlyoffice { customUserAgent = NCUtility.shared.getCustomUserAgentOnlyOffice() - } + } else if editorId == NCGlobal.shared.editorText { + customUserAgent = NCUtility.shared.getCustomUserAgentNCText() + } // else: use default + NCCommunication.shared.NCTextGetListOfTemplates(customUserAgent: customUserAgent) { account, templates, errorCode, _ in self.indicator.stopAnimating() diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift index 7058688f7..565d3fc2b 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift @@ -459,6 +459,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) return } + let fileUrl = URL(fileURLWithPath: fileNameGenerateExport) // Text Recognition TXT if fileType == "TXT" && self.form.formRow(withTag: "textRecognition")!.value as! Int == 1 { @@ -489,7 +490,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC } do { - try textFile.write(to: NSURL(fileURLWithPath: fileNameGenerateExport) as URL, atomically: true, encoding: .utf8) + try textFile.write(to: fileUrl, atomically: true, encoding: .utf8) } catch { NCUtility.shared.stopActivityIndicator() NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) @@ -562,7 +563,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC UIGraphicsEndPDFContext() do { - try pdfData.write(toFile: fileNameGenerateExport, options: .atomic) + try pdfData.write(to: fileUrl, options: .atomic) } catch { print("error catched") } @@ -579,13 +580,14 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC } do { - try data.write(to: NSURL.fileURL(withPath: fileNameGenerateExport), options: .atomic) + try data.write(to: fileUrl, options: .atomic) } catch { NCUtility.shared.stopActivityIndicator() 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() diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift index a262b4dc8..795dadedd 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift @@ -245,6 +245,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload + metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath) if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: fileNameSave) != nil { diff --git a/iOSClient/Main/NCFunctionCenter.swift b/iOSClient/Main/NCFunctionCenter.swift index 55378f930..52e62224f 100644 --- a/iOSClient/Main/NCFunctionCenter.swift +++ b/iOSClient/Main/NCFunctionCenter.swift @@ -77,11 +77,8 @@ import SVGKit editingMode = true } - let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: editingMode, metadata: metadata) - let navigationController = UINavigationController(rootViewController: viewerQuickLook) - navigationController.modalPresentationStyle = .overFullScreen - - self.appDelegate.window?.rootViewController?.present(navigationController, animated: true) + let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), isEditingEnabled: editingMode, metadata: metadata) + self.appDelegate.window?.rootViewController?.present(viewerQuickLook, animated: true) case NCGlobal.shared.selectorLoadFileView: guard UIApplication.shared.applicationState == UIApplication.State.active else { break } @@ -309,27 +306,18 @@ import SVGKit func saveAlbum(metadata: tableMetadata) { let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! - let status = PHPhotoLibrary.authorizationStatus() - - if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && status == PHAuthorizationStatus.authorized { - if let image = UIImage(contentsOfFile: fileNamePath) { - UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil) - } else { - NCContentPresenter.shared.messageNotification("_save_selected_files_", description: "_file_not_saved_cameraroll_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved) + NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: appDelegate.mainTabBar?.window?.rootViewController) { hasPermission in + guard hasPermission else { + return NCContentPresenter.shared.messageNotification("_access_photo_not_enabled_", description: "_access_photo_not_enabled_msg_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved) } - - } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && status == PHAuthorizationStatus.authorized { - - if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(fileNamePath) { - UISaveVideoAtPathToSavedPhotosAlbum(fileNamePath, self, #selector(saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil) + if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, let image = UIImage(contentsOfFile: fileNamePath) { + UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil) + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue, UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(fileNamePath) { + UISaveVideoAtPathToSavedPhotosAlbum(fileNamePath, self, #selector(self.saveAlbum(_:didFinishSavingWithError:contextInfo:)), nil) } else { NCContentPresenter.shared.messageNotification("_save_selected_files_", description: "_file_not_saved_cameraroll_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved) } - - } else if status != PHAuthorizationStatus.authorized { - - NCContentPresenter.shared.messageNotification("_access_photo_not_enabled_", description: "_access_photo_not_enabled_msg_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorFileNotSaved) } } @@ -547,19 +535,19 @@ import SVGKit } } - func openSelectView(items: [Any]) { + func openSelectView(items: [tableMetadata]) { let navigationController = UIStoryboard(name: "NCSelect", bundle: nil).instantiateInitialViewController() as! UINavigationController let topViewController = navigationController.topViewController as! NCSelect var listViewController = [NCSelect]() - var copyItems: [Any] = [] + var copyItems: [tableMetadata] = [] for item in items { copyItems.append(item) } let homeUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) - var serverUrl = (copyItems[0] as! Nextcloud.tableMetadata).serverUrl + var serverUrl = copyItems[0].serverUrl // Setup view controllers such that the current view is of the same directory the items to be copied are in while true { @@ -628,7 +616,8 @@ import SVGKit } } let titleOffline = isOffline ? NSLocalizedString("_remove_available_offline_", comment: "") : NSLocalizedString("_set_available_offline_", comment: "") - + let titleLock = metadata.lock ? NSLocalizedString("_unlock_file_", comment: "") : NSLocalizedString("_lock_file_", comment: "") + let iconLock = metadata.lock ? "lock.open" : "lock" let copy = UIAction(title: NSLocalizedString("_copy_file_", comment: ""), image: UIImage(systemName: "doc.on.doc")) { _ in self.copyPasteboard(pasteboardOcIds: [metadata.ocId], hudView: viewController.view) } @@ -649,7 +638,10 @@ import SVGKit viewController.reloadDataSource() } } - + + let lockUnlock = UIAction(title: titleLock, image: UIImage(systemName: iconLock)) { _ in + NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !metadata.lock) + } let save = UIAction(title: titleSave, image: UIImage(systemName: "square.and.arrow.down")) { _ in if metadataMOV != nil { self.saveLivePhoto(metadata: metadata, metadataMOV: metadataMOV!) @@ -745,15 +737,29 @@ import SVGKit // DIR - if metadata.directory { - + guard !metadata.directory else { let submenu = UIMenu(title: "", options: .displayInline, children: [favorite, offline, rename, moveCopy, copyPath, delete]) + guard appDelegate.disableSharesView == false else { return submenu } return UIMenu(title: "", children: [detail, submenu]) } // FILE - var children: [UIMenuElement] = [favorite, offline, openIn, rename, moveCopy, copy, copyPath, delete] + var children: [UIMenuElement] = [offline, openIn, moveCopy, copy, copyPath] + + if !metadata.lock { + // Workaround: PROPPATCH doesn't work (favorite) + // https://github.com/nextcloud/files_lock/issues/68 + children.insert(favorite, at: 0) + children.append(delete) + children.insert(rename, at: 3) + } else if enableDeleteLocal { + children.append(deleteConfirmLocal) + } + + if NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesLockVersion) >= 1, metadata.canUnlock(as: appDelegate.userId) { + children.insert(lockUnlock, at: metadata.lock ? 0 : 1) + } if (metadata.contentType != "image/svg+xml") && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue) { children.insert(save, at: 2) @@ -768,22 +774,23 @@ import SVGKit } if enableViewInFolder { - children.insert(viewInFolder, at: children.count-1) + children.insert(viewInFolder, at: children.count - 1) } 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) { - children.insert(modify, at: children.count-1) + 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) + 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/NCMainTabBar.swift b/iOSClient/Main/NCMainTabBar.swift index 7a3dd50fa..ecdc124d5 100644 --- a/iOSClient/Main/NCMainTabBar.swift +++ b/iOSClient/Main/NCMainTabBar.swift @@ -144,7 +144,7 @@ class NCMainTabBar: UITabBar { // Favorite if let item = items?[1] { item.title = NSLocalizedString("_favorites_", comment: "") - item.image = UIImage(named: "tabBarFavorites")?.image(color: NCBrandColor.shared.brandElement, size: 25) + item.image = UIImage(named: "star.fill")?.image(color: NCBrandColor.shared.brandElement, size: 25) item.selectedImage = item.image } @@ -158,7 +158,7 @@ class NCMainTabBar: UITabBar { // Media if let item = items?[3] { item.title = NSLocalizedString("_media_", comment: "") - item.image = UIImage(named: "tabBarMedia")?.image(color: NCBrandColor.shared.brandElement, size: 25) + item.image = UIImage(named: "media")?.image(color: NCBrandColor.shared.brandElement, size: 25) item.selectedImage = item.image } diff --git a/iOSClient/Media/NCMedia.swift b/iOSClient/Media/NCMedia.swift index aebb00c67..c26d26385 100644 --- a/iOSClient/Media/NCMedia.swift +++ b/iOSClient/Media/NCMedia.swift @@ -725,10 +725,11 @@ class NCMediaCommandView: UIView { moreView.layer.masksToBounds = true controlButtonView.layer.cornerRadius = 20 controlButtonView.layer.masksToBounds = true + controlButtonView.effect = UIBlurEffect(style: .dark) gradient.frame = bounds - gradient.startPoint = CGPoint(x: 0, y: 0.50) - gradient.endPoint = CGPoint(x: 0, y: 0.9) - gradient.colors = [UIColor.black.withAlphaComponent(0.4).cgColor, UIColor.clear.cgColor] + gradient.startPoint = CGPoint(x: 0, y: 0.5) + gradient.endPoint = CGPoint(x: 0, y: 1) + gradient.colors = [UIColor.black.withAlphaComponent(UIAccessibility.isReduceTransparencyEnabled ? 0.8 : 0.4).cgColor, UIColor.clear.cgColor] layer.insertSublayer(gradient, at: 0) moreButton.setImage(UIImage(named: "more")!.image(color: .white, size: 25), for: .normal) title.text = "" @@ -743,7 +744,7 @@ class NCMediaCommandView: UIView { } } else { UIView.animate(withDuration: 0.3) { - self.moreView.effect = UIBlurEffect(style: .regular) + self.moreView.effect = UIBlurEffect(style: .dark) self.gradient.isHidden = false self.controlButtonView.isHidden = false } diff --git a/iOSClient/Menu/AppDelegate+Menu.swift b/iOSClient/Menu/AppDelegate+Menu.swift index 6c693c466..f17217067 100644 --- a/iOSClient/Menu/AppDelegate+Menu.swift +++ b/iOSClient/Menu/AppDelegate+Menu.swift @@ -118,29 +118,8 @@ extension AppDelegate { actions.append( NCMenuAction(title: NSLocalizedString("_create_folder_", comment: ""), icon: UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in - - if appDelegate.activeServerUrl == "" { return } - - let alertController = UIAlertController(title: NSLocalizedString("_create_folder_on_", comment: ""), message: nil, preferredStyle: .alert) - - alertController.addTextField { textField in - textField.autocapitalizationType = UITextAutocapitalizationType.sentences - } - - let cancelAction = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: nil) - let okAction = UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in - if let fileNameFolder = alertController.textFields?.first?.text { - NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: appDelegate.activeServerUrl, account: appDelegate.account, urlBase: appDelegate.urlBase, overwrite: false) { errorCode, errorDescription in - if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } - } - } - }) - - alertController.addAction(cancelAction) - alertController.addAction(okAction) - + guard !appDelegate.activeServerUrl.isEmpty else { return } + let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate) appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil) } ) diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index 2cfb5e7d3..fda43661f 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -71,22 +71,59 @@ extension NCCollectionViewCommon { ) ) + if metadata.lock { + var lockOwnerName = metadata.lockOwnerDisplayName.isEmpty ? metadata.lockOwner : metadata.lockOwnerDisplayName + var lockIcon = NCUtility.shared.loadUserImage(for: metadata.lockOwner, displayName: lockOwnerName, userBaseUrl: metadata) + if metadata.lockOwnerType != 0 { + lockOwnerName += " app" + if !metadata.lockOwnerEditor.isEmpty, let appIcon = UIImage(named: metadata.lockOwnerEditor) { + lockIcon = appIcon + } + } + + var lockTimeString: String? + if let lockTime = metadata.lockTimeOut { + if lockTime >= Date().addingTimeInterval(60), + let timeInterval = (lockTime.timeIntervalSince1970 - Date().timeIntervalSince1970).format() { + lockTimeString = String(format: NSLocalizedString("_time_remaining_", comment: ""), timeInterval) + } else if lockTime > Date() { + lockTimeString = NSLocalizedString("_less_a_minute_", comment: "") + } // else: don't show negative time + } + if let lockTime = metadata.lockTime, lockTimeString == nil { + lockTimeString = DateFormatter.localizedString(from: lockTime, dateStyle: .short, timeStyle: .short) + } + + actions.append( + NCMenuAction( + title: String(format: NSLocalizedString("_locked_by_", comment: ""), lockOwnerName), + details: lockTimeString, + icon: lockIcon, + action: nil) + ) + } + + actions.append(.seperator) + // // FAVORITE - // - actions.append( - NCMenuAction( - title: metadata.favorite ? NSLocalizedString("_remove_favorites_", comment: "") : NSLocalizedString("_add_favorites_", comment: ""), - icon: NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.yellowFavorite), - action: { _ in - NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in - if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + // FIXME: PROPPATCH doesn't work + // https://github.com/nextcloud/files_lock/issues/68 + if !metadata.lock { + actions.append( + NCMenuAction( + title: metadata.favorite ? NSLocalizedString("_remove_favorites_", comment: "") : NSLocalizedString("_add_favorites_", comment: ""), + icon: NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.yellowFavorite), + action: { _ in + NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in + if errorCode != 0 { + NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } } } - } + ) ) - ) + } // // DETAIL @@ -102,6 +139,20 @@ extension NCCollectionViewCommon { ) ) } + + // + // LOCK / UNLOCK + // + let hasLockCapability = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesLockVersion) >= 1 + if !metadata.directory, metadata.canUnlock(as: appDelegate.userId), hasLockCapability { + let lockAction = NCMenuAction.lockUnlockFiles(shouldLock: !metadata.lock, metadatas: [metadata]) + if metadata.lock { + // make unlock first action, after info rows & seperator + actions.insert(lockAction, at: 3) + } else { + actions.append(lockAction) + } + } // // OFFLINE @@ -183,7 +234,7 @@ extension NCCollectionViewCommon { // // RENAME // - if !(isFolderEncrypted && metadata.serverUrl == serverUrlHome) { + if !(isFolderEncrypted && metadata.serverUrl == serverUrlHome), !metadata.lock { actions.append( NCMenuAction( title: NSLocalizedString("_rename_", comment: ""), @@ -322,3 +373,14 @@ extension NCCollectionViewCommon { presentMenu(with: actions) } } + + +extension TimeInterval { + func format() -> String? { + let formatter = DateComponentsFormatter() + formatter.allowedUnits = [.day, .hour, .minute] + formatter.unitsStyle = .full + formatter.maximumUnitCount = 1 + return formatter.string(from: self) + } +} diff --git a/iOSClient/Menu/NCMedia+Menu.swift b/iOSClient/Menu/NCMedia+Menu.swift index 5454dd3eb..629107aff 100644 --- a/iOSClient/Menu/NCMedia+Menu.swift +++ b/iOSClient/Menu/NCMedia+Menu.swift @@ -173,8 +173,10 @@ extension NCMedia { // // DELETE - // - actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect)) + // can't delete from cache because is needed for NCMedia view, and if locked can't delete from server either. + if !selectedMetadatas.contains(where: { $0.lock && $0.lockOwner != appDelegate.userId }) { + actions.append(.deleteAction(selectedMetadatas: selectedMetadatas, metadataFolder: nil, viewController: self, completion: tapSelect)) + } } } } diff --git a/iOSClient/Menu/NCMenu+FloatingPanel.swift b/iOSClient/Menu/NCMenu+FloatingPanel.swift index 053697c80..71735a202 100644 --- a/iOSClient/Menu/NCMenu+FloatingPanel.swift +++ b/iOSClient/Menu/NCMenu+FloatingPanel.swift @@ -23,6 +23,7 @@ import Foundation import FloatingPanel +import UIKit class NCMenuFloatingPanelLayout: FloatingPanelLayout { var position: FloatingPanelPosition = .bottom @@ -37,7 +38,7 @@ class NCMenuFloatingPanelLayout: FloatingPanelLayout { let topInset: CGFloat - init(numberOfActions: Int) { + init(actionsHeight: CGFloat) { // sometimes UIScreen.main.bounds.size.height is not updated correctly // this ensures we use the correct height value // can't use `layoutFor size` since menu is dieplayed on top of the whole screen not just the VC @@ -45,7 +46,7 @@ class NCMenuFloatingPanelLayout: FloatingPanelLayout { ? min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height) : max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height) let bottomInset = UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0 - let panelHeight = CGFloat(numberOfActions * 60) + bottomInset + let panelHeight = CGFloat(actionsHeight) + bottomInset topInset = max(48, screenHeight - panelHeight) } @@ -75,5 +76,17 @@ class NCMenuPanelController: FloatingPanelController { self.isRemovalInteractionEnabled = true self.backdropView.dismissalTapGestureRecognizer.isEnabled = true self.surfaceView.layer.cornerRadius = 16 + + surfaceView.grabberHandle.accessibilityLabel = NSLocalizedString("_cart_controller_", comment: "") + + let collapseName = NSLocalizedString("_dismiss_menu_", comment: "") + let collapseAction = UIAccessibilityCustomAction(name: collapseName, target: self, selector: #selector(accessibilityActionCollapsePanel)) + + surfaceView.grabberHandle.accessibilityCustomActions = [collapseAction] + surfaceView.grabberHandle.isAccessibilityElement = true } + + @objc private func accessibilityActionCollapsePanel() { + self.dismiss(animated: true) + } } diff --git a/iOSClient/Menu/NCMenu.storyboard b/iOSClient/Menu/NCMenu.storyboard index ce18c84df..dd46fcc4a 100644 --- a/iOSClient/Menu/NCMenu.storyboard +++ b/iOSClient/Menu/NCMenu.storyboard @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dbT-V0-aXb"> +<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="dbT-V0-aXb"> <device id="retina6_1" 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="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -18,7 +18,7 @@ <color key="backgroundColor" systemColor="systemBackgroundColor"/> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="menuActionCell" rowHeight="60" id="MT1-Lu-9SA"> - <rect key="frame" x="0.0" y="28" width="414" height="60"/> + <rect key="frame" x="0.0" y="44.5" width="414" height="60"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MT1-Lu-9SA" id="tmT-MO-Dwy"> <rect key="frame" x="0.0" y="0.0" width="414" height="60"/> @@ -31,19 +31,31 @@ <constraint firstAttribute="width" constant="28" id="gxY-bI-V0v"/> </constraints> </imageView> - <label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A8f-xF-j3i"> - <rect key="frame" x="60" y="19.5" width="326" height="21"/> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="6vv-IO-HJM"> + <rect key="frame" x="60" y="19" width="326" height="22"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A8f-xF-j3i"> + <rect key="frame" x="0.0" y="0.0" width="326" height="20"/> + <fontDescription key="fontDescription" type="system" pointSize="17"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" tag="3" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="749" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SjQ-O4-Clh"> + <rect key="frame" x="0.0" y="22" width="326" height="0.0"/> + <fontDescription key="fontDescription" type="system" pointSize="15"/> + <color key="textColor" systemColor="secondaryLabelColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </stackView> </subviews> <constraints> - <constraint firstItem="A8f-xF-j3i" firstAttribute="leading" secondItem="RV0-3K-eSN" secondAttribute="trailing" constant="16" id="ADH-SJ-JNh"/> + <constraint firstAttribute="trailingMargin" secondItem="6vv-IO-HJM" secondAttribute="trailing" constant="8" id="4pu-DJ-9cK"/> + <constraint firstItem="6vv-IO-HJM" firstAttribute="leading" secondItem="RV0-3K-eSN" secondAttribute="trailing" constant="16" id="8tD-ZW-Y88"/> <constraint firstItem="RV0-3K-eSN" firstAttribute="leading" secondItem="tmT-MO-Dwy" secondAttribute="leading" constant="16" id="QQt-st-4hA"/> <constraint firstItem="RV0-3K-eSN" firstAttribute="centerY" secondItem="tmT-MO-Dwy" secondAttribute="centerY" id="R6O-om-tEz"/> - <constraint firstAttribute="trailingMargin" secondItem="A8f-xF-j3i" secondAttribute="trailing" constant="8" id="fia-KH-ier"/> - <constraint firstItem="A8f-xF-j3i" firstAttribute="centerY" secondItem="tmT-MO-Dwy" secondAttribute="centerY" id="kPV-bd-AAL"/> + <constraint firstItem="6vv-IO-HJM" firstAttribute="top" secondItem="tmT-MO-Dwy" secondAttribute="topMargin" constant="8" id="d50-c3-Ofv"/> + <constraint firstAttribute="bottomMargin" secondItem="6vv-IO-HJM" secondAttribute="bottom" constant="8" id="zte-5x-B8K"/> </constraints> </tableViewCellContentView> </tableViewCell> @@ -60,6 +72,9 @@ </scene> </scenes> <resources> + <systemColor name="secondaryLabelColor"> + <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> <systemColor name="systemBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </systemColor> diff --git a/iOSClient/Menu/NCMenu.swift b/iOSClient/Menu/NCMenu.swift index 4c0e2a50a..cf7f99e44 100644 --- a/iOSClient/Menu/NCMenu.swift +++ b/iOSClient/Menu/NCMenu.swift @@ -28,6 +28,10 @@ import UIKit import FloatingPanel +extension Array where Element == NCMenuAction { + var listHeight: CGFloat { reduce(0, { $0 + $1.rowHeight }) } +} + class NCMenu: UITableViewController { var actions = [NCMenuAction]() @@ -42,6 +46,8 @@ class NCMenu: UITableViewController { override func viewDidLoad() { super.viewDidLoad() + tableView.estimatedRowHeight = 60 + tableView.rowHeight = UITableView.automaticDimension } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -63,15 +69,25 @@ class NCMenu: UITableViewController { } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let action = actions[indexPath.row] + guard action.title != NCMenuAction.seperatorIdentifier else { + let cell = UITableViewCell() + cell.backgroundColor = NCBrandColor.shared.separator + return cell + } let cell = tableView.dequeueReusableCell(withIdentifier: "menuActionCell", for: indexPath) cell.tintColor = NCBrandColor.shared.customer - let action = actions[indexPath.row] let actionIconView = cell.viewWithTag(1) as? UIImageView let actionNameLabel = cell.viewWithTag(2) as? UILabel + let actionDetailLabel = cell.viewWithTag(3) as? UILabel if action.action == nil { cell.selectionStyle = .none } + if let details = action.details { + actionDetailLabel?.text = details + actionNameLabel?.isHidden = false + } else { actionDetailLabel?.isHidden = true } if action.isOn { actionIconView?.image = action.onIcon @@ -89,17 +105,17 @@ class NCMenu: UITableViewController { // MARK: - Tabel View Layout override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return 60 + actions[indexPath.row].title == NCMenuAction.seperatorIdentifier ? NCMenuAction.seperatorHeight : UITableView.automaticDimension } } extension NCMenu: FloatingPanelControllerDelegate { func floatingPanel(_ fpc: FloatingPanelController, layoutFor size: CGSize) -> FloatingPanelLayout { - return NCMenuFloatingPanelLayout(numberOfActions: self.actions.count) + return NCMenuFloatingPanelLayout(actionsHeight: self.actions.listHeight) } func floatingPanel(_ fpc: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout { - return NCMenuFloatingPanelLayout(numberOfActions: self.actions.count) + return NCMenuFloatingPanelLayout(actionsHeight: self.actions.listHeight) } func floatingPanel(_ fpc: FloatingPanelController, animatorForDismissingWith velocity: CGVector) -> UIViewPropertyAnimator { diff --git a/iOSClient/Menu/NCMenuAction.swift b/iOSClient/Menu/NCMenuAction.swift index 03f17b2c1..6aff1a224 100644 --- a/iOSClient/Menu/NCMenuAction.swift +++ b/iOSClient/Menu/NCMenuAction.swift @@ -5,12 +5,29 @@ // Created by Henrik Storch on 17.02.22. // Copyright © 2022 Marino Faggiana. All rights reserved. // +// Author Henrik Storch <henrik.storch@nextcloud.com> +// 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 NCMenuAction { let title: String + let details: String? let icon: UIImage let selectable: Bool var onTitle: String? @@ -18,16 +35,19 @@ class NCMenuAction { var selected: Bool = false var isOn: Bool = false var action: ((_ menuAction: NCMenuAction) -> Void)? + var rowHeight: CGFloat { self.title == NCMenuAction.seperatorIdentifier ? NCMenuAction.seperatorHeight : self.details != nil ? 80 : 60 } - init(title: String, icon: UIImage, action: ((_ menuAction: NCMenuAction) -> Void)?) { + init(title: String, details: String? = nil, icon: UIImage, action: ((_ menuAction: NCMenuAction) -> Void)?) { self.title = title + self.details = details self.icon = icon self.action = action self.selectable = false } - init(title: String, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, action: ((_ menuAction: NCMenuAction) -> Void)?) { + init(title: String, details: String? = nil, icon: UIImage, onTitle: String? = nil, onIcon: UIImage? = nil, selected: Bool, on: Bool, action: ((_ menuAction: NCMenuAction) -> Void)?) { self.title = title + self.details = details self.icon = icon self.onTitle = onTitle ?? title self.onIcon = onIcon ?? icon @@ -41,6 +61,13 @@ class NCMenuAction { // MARK: - Actions extension NCMenuAction { + static let seperatorIdentifier = "NCMenuAction.SEPARATOR" + static let seperatorHeight: CGFloat = 0.5 + + /// A static seperator, with no actions, text, or icons + static var seperator: NCMenuAction { + return NCMenuAction(title: seperatorIdentifier, icon: UIImage(), action: nil) + } /// Select all items static func selectAllAction(action: @escaping () -> Void) -> NCMenuAction { @@ -86,6 +113,7 @@ extension NCMenuAction { } } // else: no metadata selected + let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock } var fileList = "" for (ix, metadata) in selectedMetadatas.enumerated() { guard ix < 3 else { fileList += "\n - ..."; break } @@ -100,10 +128,12 @@ extension NCMenuAction { title: titleDelete, message: NSLocalizedString("_want_delete_", comment: "") + fileList, preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in - selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: false) }) - completion?() - }) + if canDeleteServer { + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in + selectedMetadatas.forEach({ NCOperationQueue.shared.delete(metadata: $0, onlyLocalCache: false) }) + completion?() + }) + } // NCMedia removes image from collection view if removed from cache if !(viewController is NCMedia) { @@ -212,4 +242,25 @@ extension NCMenuAction { } ) } + + /// Lock or unlock a file using *files_lock* + static func lockUnlockFiles(shouldLock: Bool, metadatas: [tableMetadata], completion: (() -> Void)? = nil) -> NCMenuAction { + let titleKey: String + if metadatas.count == 1 { + titleKey = shouldLock ? "_lock_file_" : "_unlock_file_" + } else { + titleKey = shouldLock ? "_lock_selected_files_" : "_unlock_selected_files_" + } + let imageName = !shouldLock ? "lock_open" : "lock" + return NCMenuAction( + title: NSLocalizedString(titleKey, comment: ""), + icon: NCUtility.shared.loadImage(named: imageName), + action: { _ in + for metadata in metadatas where metadata.lock != shouldLock { + NCNetworking.shared.lockUnlockFile(metadata, shoulLock: shouldLock) + } + completion?() + } + ) + } } diff --git a/iOSClient/Menu/NCShare+Menu.swift b/iOSClient/Menu/NCShare+Menu.swift new file mode 100644 index 000000000..0ec28c6e3 --- /dev/null +++ b/iOSClient/Menu/NCShare+Menu.swift @@ -0,0 +1,121 @@ +// +// NCShare+Menu.swift +// Nextcloud +// +// Created by Henrik Storch on 16.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 NCShare { + func toggleShareMenu(for share: tableShare) { + + var actions = [NCMenuAction]() + + if share.shareType == 3, canReshare { + actions.append( + NCMenuAction( + title: NSLocalizedString("_share_add_sharelink_", comment: ""), + icon: NCUtility.shared.loadImage(named: "shareAdd"), + action: { _ in + self.makeNewLinkShare() + } + ) + ) + } + + actions.append( + NCMenuAction( + title: NSLocalizedString("_details_", comment: ""), + icon: NCUtility.shared.loadImage(named: "pencil"), + action: { _ in + guard + let advancePermission = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "NCShareAdvancePermission") as? NCShareAdvancePermission, + let navigationController = self.navigationController, !share.isInvalidated else { return } + advancePermission.networking = self.networking + advancePermission.share = tableShare(value: share) + advancePermission.oldTableShare = tableShare(value: share) + advancePermission.metadata = self.metadata + navigationController.pushViewController(advancePermission, animated: true) + } + ) + ) + + actions.append( + NCMenuAction( + title: NSLocalizedString("_share_unshare_", comment: ""), + icon: NCUtility.shared.loadImage(named: "trash"), + action: { _ in + self.networking?.unShare(idShare: share.idShare) + } + ) + ) + + self.presentMenu(with: actions) + } + + func toggleUserPermissionMenu(isDirectory: Bool, tableShare: tableShare) { + var actions = [NCMenuAction]() + + actions.append( + NCMenuAction( + title: NSLocalizedString("_share_read_only_", comment: ""), + icon: NCUtility.shared.loadImage(named: "eye"), + selected: tableShare.permissions == (NCGlobal.shared.permissionReadShare + NCGlobal.shared.permissionShareShare) || tableShare.permissions == NCGlobal.shared.permissionReadShare, + on: false, + action: { _ in + let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) + let permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: canShare, andIsFolder: isDirectory) + self.updateSharePermissions(share: tableShare, permissions: permissions) + } + ) + ) + + actions.append( + NCMenuAction( + title: isDirectory ? NSLocalizedString("_share_allow_upload_", comment: "") : NSLocalizedString("_share_editing_", comment: ""), + icon: NCUtility.shared.loadImage(named: "pencil"), + selected: hasUploadPermission(tableShare: tableShare), + on: false, + action: { _ in + let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) + let permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: canShare, andIsFolder: isDirectory) + self.updateSharePermissions(share: tableShare, permissions: permissions) + } + ) + ) + + self.presentMenu(with: actions) + } + + fileprivate func hasUploadPermission(tableShare: tableShare) -> Bool { + let uploadPermissions = [ + NCGlobal.shared.permissionMaxFileShare, + NCGlobal.shared.permissionMaxFolderShare, + NCGlobal.shared.permissionDefaultFileRemoteShareNoSupportShareOption, + NCGlobal.shared.permissionDefaultFolderRemoteShareNoSupportShareOption] + return uploadPermissions.contains(tableShare.permissions) + } + + func updateSharePermissions(share: tableShare, permissions: Int) { + let updatedShare = tableShare(value: share) + updatedShare.permissions = permissions + networking?.updateShare(option: updatedShare) + } +} diff --git a/iOSClient/Menu/NCViewer+Menu.swift b/iOSClient/Menu/NCViewer+Menu.swift index e85354bce..5b18d6a6f 100644 --- a/iOSClient/Menu/NCViewer+Menu.swift +++ b/iOSClient/Menu/NCViewer+Menu.swift @@ -40,20 +40,23 @@ extension NCViewer { // // FAVORITE - // - actions.append( - NCMenuAction( - title: titleFavorite, - icon: NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.yellowFavorite), - action: { _ in - NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in - if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + // Workaround: PROPPATCH doesn't work + // https://github.com/nextcloud/files_lock/issues/68 + if !metadata.lock { + actions.append( + NCMenuAction( + title: titleFavorite, + icon: NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.yellowFavorite), + action: { _ in + NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in + if errorCode != 0 { + NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } } } - } + ) ) - ) + } // // DETAIL @@ -102,7 +105,7 @@ extension NCViewer { // // CONVERSION VIDEO TO MPEG4 (MFFF Lib) // - #if MFFFLIB +#if MFFFLIB if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { actions.append( @@ -117,7 +120,7 @@ extension NCViewer { ) ) } - #endif +#endif // // SAVE IMAGE / VIDEO @@ -146,7 +149,7 @@ extension NCViewer { // // RENAME // - if !webView { + if !webView, !metadata.lock { actions.append( NCMenuAction( title: NSLocalizedString("_rename_", comment: ""), @@ -226,31 +229,6 @@ extension NCViewer { ) ) - var title = "" - var icon = UIImage() - - if CCUtility.getPDFDisplayDirection() == .horizontal { - title = NSLocalizedString("_pdf_vertical_", comment: "") - icon = UIImage(named: "pdf-vertical")!.image(color: NCBrandColor.shared.gray, size: 50) - } else { - title = NSLocalizedString("_pdf_horizontal_", comment: "") - icon = UIImage(named: "pdf-horizontal")!.image(color: NCBrandColor.shared.gray, size: 50) - } - - actions.append( - NCMenuAction( - title: title, - icon: icon, - action: { _ in - if CCUtility.getPDFDisplayDirection() == .horizontal { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection, userInfo: ["direction": PDFDisplayDirection.vertical]) - } else { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection, userInfo: ["direction": PDFDisplayDirection.horizontal]) - } - } - ) - ) - actions.append( NCMenuAction( title: NSLocalizedString("_go_to_page_", comment: ""), diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index bae04f0cc..af5f23ab2 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -112,7 +112,7 @@ class NCGlobal: NSObject { // Database Realm // let databaseDefault = "nextcloud.realm" - let databaseSchemaVersion: UInt64 = 216 + let databaseSchemaVersion: UInt64 = 222 // Intro selector // @@ -176,6 +176,7 @@ class NCGlobal: NSObject { // let buttonMoreMore = "more" let buttonMoreStop = "stop" + let buttonMoreLock = "moreLock" // Text - OnlyOffice - Collabora - QuickLook // @@ -344,15 +345,12 @@ class NCGlobal: NSObject { let notificationCenterFavoriteFile = "favoriteFile" // userInfo: ocId let notificationCenterMenuSearchTextPDF = "menuSearchTextPDF" - let notificationCenterMenuPDFDisplayDirection = "menuPDFDisplayDirection" // userInfo: direction let notificationCenterMenuGotToPageInPDF = "menuGotToPageInPDF" let notificationCenterMenuDetailClose = "menuDetailClose" let notificationCenterChangedLocation = "changedLocation" let notificationStatusAuthorizationChangedLocation = "statusAuthorizationChangedLocation" - let notificationCenterShareChangePermissions = "shareChangePermissions" // userInfo: idShare, permissions, hideDownload - let notificationCenterDownloadedThumbnail = "DownloadedThumbnail" // userInfo: ocId let notificationCenterHidePlayerToolBar = "hidePlayerToolBar" // userInfo: ocId @@ -362,4 +360,8 @@ class NCGlobal: NSObject { let notificationCenterReloadMediaPage = "reloadMediaPage" let notificationCenterPlayMedia = "playMedia" let notificationCenterPauseMedia = "pauseMedia" + + // Tip + // + let tipNCViewerPDFThumbnail = "tipncviewerpdfthumbnail" } diff --git a/iOSClient/Networking/NCAutoUpload.swift b/iOSClient/Networking/NCAutoUpload.swift index ce5017567..2e080974c 100644 --- a/iOSClient/Networking/NCAutoUpload.swift +++ b/iOSClient/Networking/NCAutoUpload.swift @@ -25,78 +25,17 @@ import UIKit import CoreLocation import NCCommunication -class NCAutoUpload: NSObject, CLLocationManagerDelegate { +class NCAutoUpload: NSObject { @objc static let shared: NCAutoUpload = { let instance = NCAutoUpload() return instance }() private let appDelegate = UIApplication.shared.delegate as! AppDelegate - public var locationManager: CLLocationManager? private var endForAssetToUpload: Bool = false // MARK: - - @objc func startSignificantChangeUpdates() { - - if locationManager == nil { - - locationManager = CLLocationManager() - locationManager?.delegate = self - locationManager?.distanceFilter = 10 - } - - locationManager?.requestAlwaysAuthorization() - locationManager?.startMonitoringSignificantLocationChanges() - } - - @objc func stopSignificantChangeUpdates() { - - locationManager?.stopMonitoringSignificantLocationChanges() - } - - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - - let location = locations.last - guard let latitude = location?.coordinate.latitude else { return } - guard let longitude = location?.coordinate.longitude else { return } - - NCCommunicationCommon.shared.writeLog("Location manager: latitude \(latitude) longitude \(longitude)") - - if let activeAccount = NCManageDatabase.shared.getActiveAccount() { - if activeAccount.autoUpload && activeAccount.autoUploadBackground && UIApplication.shared.applicationState == UIApplication.State.background { - NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: nil) { hasPermission in - if hasPermission { - self.uploadAssetsNewAndFull(viewController: nil, selector: NCGlobal.shared.selectorUploadAutoUpload, log: "Change location") { items in - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber) - if items > 0 { - self.appDelegate.networkingProcessUpload?.startProcess() - } - } - } - } - } - } - } - - func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { - if CLLocationManager.authorizationStatus() != CLAuthorizationStatus.authorizedAlways { - NCManageDatabase.shared.setAccountAutoUploadProperty("autoUploadBackground", state: false) - self.stopSignificantChangeUpdates() - } - } - - func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { - NCAskAuthorization.shared.askAuthorizationLocationManager { hasFullPermissions in - if !hasFullPermissions { - NCManageDatabase.shared.setAccountAutoUploadProperty("autoUploadBackground", state: false) - self.stopSignificantChangeUpdates() - } - } - } - - // MARK: - - @objc func initAutoUpload(viewController: UIViewController?, completion: @escaping (_ items: Int) -> Void) { if let activeAccount = NCManageDatabase.shared.getActiveAccount() { if activeAccount.autoUpload { @@ -108,19 +47,9 @@ class NCAutoUpload: NSObject, CLLocationManagerDelegate { } completion(items) } - if activeAccount.autoUploadBackground { - NCAskAuthorization.shared.askAuthorizationLocationManager { hasFullPermissions in - if hasFullPermissions { - self.startSignificantChangeUpdates() - } else { - NCManageDatabase.shared.setAccountAutoUploadProperty("autoUploadBackground", state: false) - self.stopSignificantChangeUpdates() - } - } - } + } else { NCManageDatabase.shared.setAccountAutoUploadProperty("autoUpload", state: false) - self.stopSignificantChangeUpdates() completion(0) } } @@ -128,7 +57,6 @@ class NCAutoUpload: NSObject, CLLocationManagerDelegate { completion(0) } } else { - stopSignificantChangeUpdates() completion(0) } } @@ -241,7 +169,6 @@ class NCAutoUpload: NSObject, CLLocationManagerDelegate { metadataForUpload.assetLocalIdentifier = asset.localIdentifier metadataForUpload.session = session metadataForUpload.sessionSelector = selector - metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(asset: asset) metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload if assetMediaType == PHAssetMediaType.video { metadataForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue @@ -250,7 +177,7 @@ class NCAutoUpload: NSObject, CLLocationManagerDelegate { } if selector == NCGlobal.shared.selectorUploadAutoUpload { - NCCommunicationCommon.shared.writeLog("Automatic upload added \(metadataForUpload.fileNameView) (\(metadataForUpload.size) bytes) with Identifier \(metadataForUpload.assetLocalIdentifier)") + NCCommunicationCommon.shared.writeLog("Automatic upload added \(metadataForUpload.fileNameView) with Identifier \(metadataForUpload.assetLocalIdentifier)") self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload], verifyAlreadyExists: true) NCManageDatabase.shared.addPhotoLibrary([asset], account: account.account) } else if selector == NCGlobal.shared.selectorUploadAutoUploadAll { @@ -276,7 +203,7 @@ class NCAutoUpload: NSObject, CLLocationManagerDelegate { metadataForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue if selector == NCGlobal.shared.selectorUploadAutoUpload { - NCCommunicationCommon.shared.writeLog("Automatic upload added Live Photo \(metadataForUpload.fileNameView) (\(metadataForUpload.size) bytes) with Identifier \(metadataForUpload.assetLocalIdentifier)") + 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 { diff --git a/iOSClient/Networking/NCNetworking.swift b/iOSClient/Networking/NCNetworking.swift index 19dccaedc..84c074ac9 100644 --- a/iOSClient/Networking/NCNetworking.swift +++ b/iOSClient/Networking/NCNetworking.swift @@ -413,10 +413,24 @@ import Queuer @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) - + + 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 @@ -448,7 +462,7 @@ import Queuer let metadata = tableMetadata.init(value: metadata) - if CCUtility.fileProviderStorageExists(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) @@ -467,7 +481,7 @@ import Queuer } else { - CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadata, notification: true) { extractMetadata, fileNamePath in + CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadata) { extractMetadata, fileNamePath in guard let metadata = extractMetadata else { NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) @@ -523,8 +537,9 @@ import Queuer }) { _, ocId, etag, date, size, _, _, errorCode, errorDescription in self.uploadRequest[fileNameLocalPath] = nil - self.uploadComplete(fileName: metadata.fileName, serverUrl: metadata.serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: uploadTask!, errorCode: errorCode, errorDescription: errorDescription) - + 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) + } completion(errorCode, errorDescription) } } @@ -545,7 +560,7 @@ import Queuer start() // Check file dim > 0 - if NCUtilityFileSystem.shared.getFileSize(filePath: fileNameLocalPath) == 0 { + if NCUtilityFileSystem.shared.getFileSize(filePath: fileNameLocalPath) == 0 && metadata.size != 0 { NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) @@ -570,97 +585,87 @@ import Queuer } func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: NSDate?, size: Int64, description: String?, task: URLSessionTask, errorCode: Int, errorDescription: String) { - if delegate != nil { + 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) - } else { - - guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(description) else { return } - guard let tableAccount = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", metadata.account)) else { return } - let ocIdTemp = metadata.ocId - var errorDescription = errorDescription - - if errorCode == 0 && ocId != nil && size > 0 { - - let metadata = tableMetadata.init(value: metadata) + return + } - NCUtilityFileSystem.shared.moveFileInBackground(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId)) + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(description) else { return } + let ocIdTemp = metadata.ocId + var errorDescription = errorDescription - metadata.uploadDate = date ?? NSDate() - metadata.etag = etag ?? "" - metadata.ocId = ocId! + if errorCode == 0, let ocId = ocId, size == metadata.size { - if let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) { - metadata.fileId = fileId - } + let metadata = tableMetadata.init(value: metadata) - metadata.session = "" - metadata.sessionError = "" - metadata.sessionTaskIdentifier = 0 - metadata.status = NCGlobal.shared.metadataStatusNormal + NCUtilityFileSystem.shared.moveFileInBackground(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId)) - // Delete Asset on Photos album - if tableAccount.autoUploadDeleteAssetLocalIdentifier && metadata.assetLocalIdentifier != "" && metadata.sessionSelector == NCGlobal.shared.selectorUploadAutoUpload { - metadata.deleteAssetLocalIdentifier = true - } + metadata.uploadDate = date ?? NSDate() + metadata.etag = etag ?? "" + metadata.ocId = ocId - if CCUtility.getDisableLocalCacheAfterUpload() { - CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) - } else { - NCManageDatabase.shared.addLocalFile(metadata: metadata) - } - NCManageDatabase.shared.addMetadata(metadata) - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) + if let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) { + metadata.fileId = fileId + } - #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 + metadata.session = "" + metadata.sessionError = "" + metadata.sessionTaskIdentifier = 0 + metadata.status = NCGlobal.shared.metadataStatusNormal - 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": ""]) + // Delete Asset on Photos album + if CCUtility.getRemovePhotoCameraRoll() && !metadata.assetLocalIdentifier.isEmpty { + metadata.deleteAssetLocalIdentifier = true + } + if CCUtility.getDisableLocalCacheAfterUpload() { + CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) } else { + NCManageDatabase.shared.addLocalFile(metadata: metadata) + } + NCManageDatabase.shared.addMetadata(metadata) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) - 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]) - - } else if errorCode == 401 || errorCode == 403 { - - #if !EXTENSION - NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: metadata.account, errorCode: errorCode, errorDescription: errorDescription) - #endif +#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 - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) + 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 { - } else { + CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - if size == 0 { - errorDescription = "File length 0" - NCCommunicationCommon.shared.writeLog("Upload error 0 length " + serverUrl + "/" + fileName + ", result: success(\(size) bytes)") - } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) + } 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)") } - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) } - - #if !EXTENSION - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil } - #endif - - // Delete - self.uploadMetadataInBackground[fileName+serverUrl] = nil + 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 } func uploadProgress(_ progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String, session: URLSession, task: URLSessionTask) { @@ -1177,6 +1182,23 @@ import Queuer } } + // MARK: - Lock Files + + @objc func lockUnlockFile(_ metadata: tableMetadata, shoulLock: Bool) { + NCCommunication.shared.lockUnlockFile(serverUrlFileName: metadata.serverUrl + "/" + metadata.fileName, shouldLock: shoulLock) { errorCode, errorDescription in + // 0: lock was successful; 412: lock did not change, no error, refresh + guard errorCode == 0 || errorCode == 412 else { + 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 + guard errorCode == 0, let metadata = metadata else { return } + NCManageDatabase.shared.addMetadata(metadata) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) + } + } + } + // MARK: - WebDav Rename @objc func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, viewController: UIViewController?, completion: @escaping (_ errorCode: Int, _ errorDescription: String?) -> Void) { diff --git a/iOSClient/Networking/NCNetworkingChunkedUpload.swift b/iOSClient/Networking/NCNetworkingChunkedUpload.swift index f9a134c7a..684484fe9 100644 --- a/iOSClient/Networking/NCNetworkingChunkedUpload.swift +++ b/iOSClient/Networking/NCNetworkingChunkedUpload.swift @@ -149,6 +149,7 @@ extension NCNetworking { if errorCode == 0 { let serverUrl = metadata.serverUrl + let assetLocalIdentifier = metadata.assetLocalIdentifier NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId) @@ -158,6 +159,11 @@ extension NCNetworking { if errorCode == 0, let metadata = metadata { + 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]) diff --git a/iOSClient/Networking/NCNetworkingProcessUpload.swift b/iOSClient/Networking/NCNetworkingProcessUpload.swift index 5b7bb01e5..b2f5eac52 100644 --- a/iOSClient/Networking/NCNetworkingProcessUpload.swift +++ b/iOSClient/Networking/NCNetworkingProcessUpload.swift @@ -160,11 +160,9 @@ 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, sessionSelector: NCGlobal.shared.selectorUploadAutoUpload) { + self.deleteAssetLocalIdentifiers(account: self.appDelegate.account) { self.startTimer() } } else { @@ -173,7 +171,7 @@ class NCNetworkingProcessUpload: NSObject { } } - private func deleteAssetLocalIdentifiers(account: String, sessionSelector: String, completition: @escaping () -> Void) { + private func deleteAssetLocalIdentifiers(account: String, completition: @escaping () -> Void) { if UIApplication.shared.applicationState != .active { completition() @@ -184,7 +182,7 @@ class NCNetworkingProcessUpload: NSObject { completition() return } - let localIdentifiers = NCManageDatabase.shared.getAssetLocalIdentifiersUploaded(account: account, sessionSelector: sessionSelector) + let localIdentifiers = NCManageDatabase.shared.getAssetLocalIdentifiersUploaded(account: account) if localIdentifiers.isEmpty { completition() return @@ -215,20 +213,7 @@ class NCNetworkingProcessUpload: NSObject { } } - // E2EE - if CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) { - metadata.e2eEncrypted = true - } - - // CHUNCK - let chunckSize = CCUtility.getChunkSize() * 1000000 - if chunckSize == 0 || metadata.size <= chunckSize { - metadatasForUpload.append(metadata) - } else { - metadata.chunk = true - metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload - metadatasForUpload.append(tableMetadata.init(value: metadata)) - } + metadatasForUpload.append(metadata) } NCManageDatabase.shared.addMetadatas(metadatasForUpload) diff --git a/iOSClient/Select/NCSelect.storyboard b/iOSClient/Select/NCSelect.storyboard index ee4fae6ba..1602e151d 100644 --- a/iOSClient/Select/NCSelect.storyboard +++ b/iOSClient/Select/NCSelect.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="EAU-PF-EEd"> +<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="EAU-PF-EEd"> <device id="retina6_5" 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"/> @@ -18,7 +18,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="0HI-k1-SD0"> - <rect key="frame" x="0.0" y="0.0" width="414" height="862"/> + <rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/> <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="D7P-75-aB1"> <size key="itemSize" width="0.0" height="0.0"/> @@ -38,7 +38,7 @@ <constraints> <constraint firstItem="vaA-85-uNN" firstAttribute="trailing" secondItem="0HI-k1-SD0" secondAttribute="trailing" id="Dk4-c3-6wl"/> <constraint firstItem="0HI-k1-SD0" firstAttribute="top" secondItem="MaM-Im-7sc" secondAttribute="top" id="GKj-QM-2Yy"/> - <constraint firstItem="vaA-85-uNN" firstAttribute="bottom" secondItem="0HI-k1-SD0" secondAttribute="bottom" id="onM-VP-itM"/> + <constraint firstAttribute="bottom" secondItem="0HI-k1-SD0" secondAttribute="bottom" id="onM-VP-itM"/> <constraint firstItem="0HI-k1-SD0" firstAttribute="leading" secondItem="vaA-85-uNN" secondAttribute="leading" id="uLL-RT-YFO"/> </constraints> </view> @@ -50,6 +50,7 @@ </barButtonItem> </navigationItem> <connections> + <outlet property="bottomContraint" destination="onM-VP-itM" id="kqE-5e-xj2"/> <outlet property="buttonCancel" destination="qHj-AF-EN7" id="Fky-XJ-BxL"/> <outlet property="collectionView" destination="0HI-k1-SD0" id="xme-mG-bnz"/> </connections> diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index 7ab1f908e..76f0ecaa5 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -30,8 +30,9 @@ import NCCommunication class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresentationControllerDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, NCEmptyDataSetDelegate { - @IBOutlet fileprivate weak var collectionView: UICollectionView! - @IBOutlet fileprivate weak var buttonCancel: UIBarButtonItem! + @IBOutlet private var collectionView: UICollectionView! + @IBOutlet private var buttonCancel: UIBarButtonItem! + @IBOutlet private var bottomContraint: NSLayoutConstraint? private var selectCommandViewSelect: NCSelectCommandView? @@ -49,7 +50,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent @objc var includeImages = false @objc var enableSelectFile = false @objc var type = "" - @objc var items: [Any] = [] + @objc var items: [tableMetadata] = [] var titleCurrentFolder = NCBrandOptions.shared.brand var serverUrl = "" @@ -79,7 +80,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent private let headerHeight: CGFloat = 50 private var headerRichWorkspaceHeight: CGFloat = 0 - private let footerHeight: CGFloat = 100 + private let footerHeight: CGFloat = 50 private var shares: [tableShare]? @@ -116,6 +117,8 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent buttonCancel.title = NSLocalizedString("_cancel_", comment: "") + bottomContraint?.constant = UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0 + // Empty emptyDataSet = NCEmptyDataSet(view: collectionView, offset: headerHeight, delegate: self) @@ -134,6 +137,8 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent selectCommandViewSelect?.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true selectCommandViewSelect?.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true selectCommandViewSelect?.heightAnchor.constraint(equalToConstant: 80).isActive = true + + bottomContraint?.constant = 80 } if typeOfCommandView == .copyMove { @@ -141,11 +146,16 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent self.view.addSubview(selectCommandViewSelect!) selectCommandViewSelect?.selectView = self selectCommandViewSelect?.translatesAutoresizingMaskIntoConstraints = false - + if items.contains(where: { $0.lock }) { + selectCommandViewSelect?.moveButton?.isEnabled = false + selectCommandViewSelect?.moveButton?.titleLabel?.isEnabled = false + } selectCommandViewSelect?.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true selectCommandViewSelect?.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true selectCommandViewSelect?.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true selectCommandViewSelect?.heightAnchor.constraint(equalToConstant: 150).isActive = true + + bottomContraint?.constant = 150 } NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) @@ -255,26 +265,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent } func createFolderButtonPressed(_ sender: UIButton) { - - let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: "", preferredStyle: .alert) - - alertController.addTextField { textField in - textField.autocapitalizationType = UITextAutocapitalizationType.words - } - - let actionSave = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default) { (_: UIAlertAction) in - if let fileName = alertController.textFields?.first?.text { - self.createFolder(with: fileName) - } - } - - let actionCancel = UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in - print("You've pressed cancel button") - } - - alertController.addAction(actionSave) - alertController.addAction(actionCancel) - + let alertController = UIAlertController.createFolder(serverUrl: serverUrl, urlBase: activeAccount) self.present(alertController, animated: true, completion: nil) } @@ -710,16 +701,6 @@ extension NCSelect { } } - func createFolder(with fileName: String) { - - NCNetworking.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: activeAccount.account, urlBase: activeAccount.urlBase) { errorCode, errorDescription in - - if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } - } - } - func loadFolder() { networkInProgress = true diff --git a/iOSClient/Settings/Acknowledgements.m b/iOSClient/Settings/Acknowledgements.m index 971bdf17f..a808f36de 100644 --- a/iOSClient/Settings/Acknowledgements.m +++ b/iOSClient/Settings/Acknowledgements.m @@ -31,12 +31,13 @@ - (void)viewDidLoad { [super viewDidLoad]; - + NSURL *rtfPath = [[NSBundle mainBundle] URLForResource:@"Acknowledgements" withExtension:@"rtf"]; NSAttributedString *attributedStringWithRtf = [[NSAttributedString alloc] initWithURL:rtfPath options:@{NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType} documentAttributes:nil error:nil]; self.txtTermini.attributedText = attributedStringWithRtf; - + + self.navigationController.navigationBar.backgroundColor = [UIColor whiteColor]; self.view.backgroundColor = [UIColor whiteColor]; self.title = NSLocalizedString(@"_acknowledgements_", nil); self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(cancelPressed)]; diff --git a/iOSClient/Settings/Acknowledgements.rtf b/iOSClient/Settings/Acknowledgements.rtf index 57bd9041d..d947cf932 100644 --- a/iOSClient/Settings/Acknowledgements.rtf +++ b/iOSClient/Settings/Acknowledgements.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2636 +{\rtf1\ansi\ansicpg1252\cocoartf2638 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;} {\colortbl;\red255\green255\blue255;} {\*\expandedcolortbl;;} @@ -8,7 +8,7 @@ \f0\fs24 \cf0 \ This software contains additional third party software. \ All the third party software included or linked is redistributed under the terms and conditions of their original licenses. \ -____________________________________________\ +__________________________________\ \ \f1\b Nextcloud iOS Communication Library\ @@ -29,12 +29,12 @@ SwiftyXMLParser - \f1\b \ SwiftyJSON - \f0\b0 MIT License (MIT) \ -____________________________________________\ +__________________________________\ \ \f1\b NYMnemonic - BIP 39 style mnemonic codes\ -\f0\b0 ____________________________________________\ +\f0\b0 __________________________________\ \ \f1\b OpenSSL\ @@ -45,7 +45,7 @@ ____________________________________________\ \f1\b \ \f0\b0 Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)\ -____________________________________________\ +__________________________________\ \ \f1\b Realm @@ -54,7 +54,7 @@ ____________________________________________\ Apache License 2.0\ \ Copyright (c) 2014-2017 https://realm.io\ -____________________________________________\ +__________________________________\ \ \f1\b UICKeyChainStore @@ -63,7 +63,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) kishikawa katsumi\ -____________________________________________\ +__________________________________\ \ \f1\b XLForm @@ -72,7 +72,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Xmartlabs ( http://xmartlabs.com )\ -____________________________________________\ +__________________________________\ \ \f1\b KTVHTTPCache @@ -81,7 +81,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) libobjc\ -____________________________________________\ +__________________________________\ \ \f1\b SVGKit @@ -90,7 +90,7 @@ ____________________________________________\ https://github.com/SVGKit/SVGKit/blob/3.x/LICENSE\ \ Copyright (c) 2010-2011 Matt Rajca, 2011-2015 various authors (c) Tipbit Inc\ -____________________________________________\ +__________________________________\ \ \f1\b SwiftRichString @@ -99,7 +99,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Daniele Margutti\ -____________________________________________\ +__________________________________\ \ \f1\b QRCodeReader @@ -108,7 +108,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Yannick Loriot\ -____________________________________________\ +__________________________________\ \ \f1\b Parchment @@ -117,16 +117,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Martin Rechsteiner\ -____________________________________________\ -\ - -\f1\b FSCalendar -\f0\b0 \ -\ -MIT License\ -\ -Copyright (c) Wenchao Ding\ -____________________________________________\ +__________________________________\ \ \f1\b DropDown @@ -135,7 +126,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Kevin Hirsch\ -____________________________________________\ +__________________________________\ \ \f1\b TLPhotoPicker @@ -144,7 +135,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) wade.hawk, junhyi.park@gmail.com\ -____________________________________________\ +__________________________________\ \ \f1\b SwiftEntryKit @@ -153,7 +144,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Daniel Huri, huri000@gmail.com\ -____________________________________________\ +__________________________________\ \ \f1\b FloatingPanel @@ -162,14 +153,14 @@ ____________________________________________\ MIT License\ \ Copyright (c) Shin Yamamoto shin@scenee.com\ -____________________________________________\ +__________________________________\ \ \f1\b MarkdownKit \f0\b0 \ \ MIT License\ -____________________________________________\ +__________________________________\ \ \f1\b Firebase Crashlytics @@ -178,7 +169,7 @@ ____________________________________________\ Apache License 2.0\ \ Copyright (c) Google Inc.\ -____________________________________________\ +__________________________________\ \ \f1\b Queuer @@ -187,7 +178,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Fabrizio Brancati\ -____________________________________________\ +__________________________________\ \ \f1\b TOPasscodeViewController @@ -196,7 +187,7 @@ ____________________________________________\ MIT License\ \ Copyright (c) Tim Oliver\ -____________________________________________\ +__________________________________\ \ \f1\b JGProgressHUD @@ -205,5 +196,15 @@ ____________________________________________\ MIT License\ \ Copyright (c) Jonas Gessner\ -____________________________________________\ +__________________________________\ +\ + +\f1\b EasyTipView +\f0\b0 \ +\ +MIT License\ +\ +Copyright (c) Teodor Patras\ +__________________________________\ +\ }
\ No newline at end of file diff --git a/iOSClient/Settings/CCAdvanced.m b/iOSClient/Settings/CCAdvanced.m index 65c255791..625e94c2d 100755 --- a/iOSClient/Settings/CCAdvanced.m +++ b/iOSClient/Settings/CCAdvanced.m @@ -54,11 +54,11 @@ [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; [section addFormRow:row]; - // Format Compatibility + Live Photo + // Format Compatibility + Live Photo + Delete asset section = [XLFormSectionDescriptor formSection]; [form addFormSection:section]; - section.footerTitle = [NSString stringWithFormat:@"%@\n%@", NSLocalizedString(@"_format_compatibility_footer_", nil), NSLocalizedString(@"_upload_mov_livephoto_footer_", nil)]; + section.footerTitle = [NSString stringWithFormat:@"%@\n%@\n%@", NSLocalizedString(@"_format_compatibility_footer_", nil), NSLocalizedString(@"_upload_mov_livephoto_footer_", nil), NSLocalizedString(@"_remove_photo_CameraRoll_desc_", nil)]; row = [XLFormRowDescriptor formRowDescriptorWithTag:@"formatCompatibility" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_format_compatibility_", nil)]; row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; @@ -75,6 +75,14 @@ [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"]; [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; [section addFormRow:row]; + + row = [XLFormRowDescriptor formRowDescriptorWithTag:@"removePhotoCameraRoll" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_remove_photo_CameraRoll_", nil)]; + row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; + if ([CCUtility getRemovePhotoCameraRoll]) 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]; // Disable Local Cache After Upload @@ -173,12 +181,8 @@ row.action.formBlock = ^(XLFormRowDescriptor * sender) { [self deselectFormRow:sender]; - - NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NCCommunicationCommon.shared.filenamePathLog] editingMode:false metadata:nil]; - UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewerQuickLook]; - navigationController.modalPresentationStyle = UIModalPresentationFullScreen; - - [self presentViewController:navigationController animated:YES completion:nil]; + NCViewerQuickLook *viewerQuickLook = [[NCViewerQuickLook alloc] initWith:[NSURL fileURLWithPath:NCCommunicationCommon.shared.filenamePathLog] isEditingEnabled:false metadata:nil]; + [self presentViewController:viewerQuickLook animated:YES completion:nil]; }; [section addFormRow:row]; @@ -355,6 +359,11 @@ [CCUtility setLivePhoto:[[rowDescriptor.value valueData] boolValue]]; } + + if ([rowDescriptor.tag isEqualToString:@"removePhotoCameraRoll"]) { + + [CCUtility setRemovePhotoCameraRoll:[[rowDescriptor.value valueData] boolValue]]; + } if ([rowDescriptor.tag isEqualToString:@"disableLocalCacheAfterUpload"]) { @@ -419,9 +428,11 @@ [CCUtility removeGroupDirectoryProviderStorage]; [CCUtility removeGroupLibraryDirectory]; - + [CCUtility removeDocumentsDirectory]; [CCUtility removeTemporaryDirectory]; + + [[NCKTVHTTPCache shared] deleteAllCache]; [CCUtility createDirectoryStandard]; diff --git a/iOSClient/Settings/CCManageAutoUpload.m b/iOSClient/Settings/CCManageAutoUpload.m index e5fe1d773..a3c9e2c02 100644 --- a/iOSClient/Settings/CCManageAutoUpload.m +++ b/iOSClient/Settings/CCManageAutoUpload.m @@ -118,34 +118,6 @@ [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; [section addFormRow:row]; - // Delete asset - - section = [XLFormSectionDescriptor formSection]; - [form addFormSection:section]; - - row = [XLFormRowDescriptor formRowDescriptorWithTag:@"removePhotoCameraRoll" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_remove_photo_CameraRoll_", nil)]; - row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; - row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; - if (activeAccount.autoUploadDeleteAssetLocalIdentifier) 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]; - - // Auto Upload Background - - section = [XLFormSectionDescriptor formSection]; - [form addFormSection:section]; - - row = [XLFormRowDescriptor formRowDescriptorWithTag:@"autoUploadBackground" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_autoupload_background_", nil)]; - row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; - row.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; - if (activeAccount.autoUploadBackground) 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]; - // Auto Upload Full section = [XLFormSectionDescriptor formSection]; [form addFormSection:section]; @@ -219,20 +191,7 @@ [super viewWillAppear:animated]; appDelegate.activeViewController = self; - - // Request permission for camera roll access - [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { - switch (status) { - case PHAuthorizationStatusRestricted: - NSLog(@"[LOG] user can't grant access to camera roll"); - break; - case PHAuthorizationStatusDenied: - NSLog(@"[LOG] user denied access to camera roll"); - break; - default: - break; - } - }]; + [[NCAskAuthorization shared] askAuthorizationPhotoLibraryWithViewController:self completion:^(BOOL status) { }]; } - (void)initialize @@ -280,41 +239,6 @@ [self reloadForm]; } - - if ([rowDescriptor.tag isEqualToString:@"removePhotoCameraRoll"]) { - - [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadDeleteAssetLocalIdentifier" state:[[rowDescriptor.value valueData] boolValue]]; - } - - if ([rowDescriptor.tag isEqualToString:@"autoUploadBackground"]) { - - if ([[rowDescriptor.value valueData] boolValue] == YES) { - - [[NCAskAuthorization shared] askAuthorizationLocationManagerWithCompletion: ^(BOOL hasFullPermissions) { - - if (hasFullPermissions == YES) { - - UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"_autoupload_background_title_", nil) message:NSLocalizedString(@"_autoupload_background_msg_", nil) preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"_ok_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {}]; - - [alertController addAction:okAction]; - [self presentViewController:alertController animated:YES completion:nil]; - - [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadBackground" state:YES]; - [[NCAutoUpload shared] startSignificantChangeUpdates]; - - } else { - - [self reloadForm]; - } - }]; - - } else { - - [[NCManageDatabase shared] setAccountAutoUploadProperty:@"autoUploadBackground" state:NO]; - [[NCAutoUpload shared] stopSignificantChangeUpdates]; - } - } if ([rowDescriptor.tag isEqualToString:@"autoUploadFull"]) { @@ -380,11 +304,7 @@ XLFormRowDescriptor *rowAutoUploadVideo = [self.form formRowWithTag:@"autoUploadVideo"]; XLFormRowDescriptor *rowAutoUploadWWAnVideo = [self.form formRowWithTag:@"autoUploadWWAnVideo"]; - - XLFormRowDescriptor *rowRemovePhotoCameraRoll = [self.form formRowWithTag:@"removePhotoCameraRoll"]; - XLFormRowDescriptor *rowAutoUploadBackground = [self.form formRowWithTag:@"autoUploadBackground"]; - XLFormRowDescriptor *rowAutoUploadFull = [self.form formRowWithTag:@"autoUploadFull"]; XLFormRowDescriptor *rowAutoUploadCreateSubfolder = [self.form formRowWithTag:@"autoUploadCreateSubfolder"]; @@ -408,13 +328,7 @@ if (activeAccount.autoUploadWWAnVideo) [rowAutoUploadWWAnVideo setValue:@1]; else [rowAutoUploadWWAnVideo setValue:@0]; - - if (activeAccount.autoUploadDeleteAssetLocalIdentifier) - [rowRemovePhotoCameraRoll setValue:@1]; else [rowRemovePhotoCameraRoll setValue:@0]; - - if (activeAccount.autoUploadBackground) - [rowAutoUploadBackground setValue:@1]; else [rowAutoUploadBackground setValue:@0]; - + if (activeAccount.autoUploadFull) [rowAutoUploadFull setValue:@1]; else [rowAutoUploadFull setValue:@0]; @@ -428,11 +342,7 @@ rowAutoUploadVideo.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; rowAutoUploadWWAnVideo.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; - - rowRemovePhotoCameraRoll.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; - rowAutoUploadBackground.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; - rowAutoUploadFull.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; rowAutoUploadCreateSubfolder.hidden = [NSString stringWithFormat:@"$%@==0", @"autoUpload"]; @@ -466,22 +376,14 @@ else sectionName = @""; break; case 4: - if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_remove_photo_CameraRoll_desc_", nil); - else sectionName = @""; - break; - case 5: - if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_description_background_", nil); - else sectionName = @""; - break; - case 6: if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_fullphotos_footer_", nil); else sectionName = @""; break; - case 7: + case 5: if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_create_subfolder_footer_", nil); else sectionName = @""; break; - case 8: + case 6: if (activeAccount.autoUpload) sectionName = NSLocalizedString(@"_autoupload_filenamemask_footer_", nil); else sectionName = @""; break; diff --git a/iOSClient/Settings/NCManageAutoUploadFileName.swift b/iOSClient/Settings/NCManageAutoUploadFileName.swift index b402f4739..3f4774c7f 100644 --- a/iOSClient/Settings/NCManageAutoUploadFileName.swift +++ b/iOSClient/Settings/NCManageAutoUploadFileName.swift @@ -213,10 +213,4 @@ class NCManageAutoUploadFileName: XLFormViewController { return String(format: NSLocalizedString("_preview_filename_", comment: ""), "MM,MMM,DD,YY,YYYY and HH,hh,mm,ss,ampm") + ":" + "\n\n" + returnString } - - // MARK: - - - override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return NCGlobal.shared.heightCellSettings - } } diff --git a/iOSClient/Settings/NCSettings.m b/iOSClient/Settings/NCSettings.m index 223241d21..6f9ab8b11 100644 --- a/iOSClient/Settings/NCSettings.m +++ b/iOSClient/Settings/NCSettings.m @@ -74,7 +74,7 @@ // Lock active YES/NO row = [XLFormRowDescriptor formRowDescriptorWithTag:@"bloccopasscode" rowType:XLFormRowDescriptorTypeButton title:NSLocalizedString(@"_lock_not_active_", nil)]; row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; - [row.cellConfig setObject:[[UIImage imageNamed:@"lock.open"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"]; + [row.cellConfig setObject:[[UIImage imageNamed:@"lock_open"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"]; [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"]; [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"]; @@ -239,7 +239,7 @@ [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"]; } else { rowBloccoPasscode.title = NSLocalizedString(@"_lock_not_active_", nil); - [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock.open"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"]; + [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock_open"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"]; } if ([CCUtility getEnableTouchFaceID]) [rowEnableTouchDaceID setValue:@1]; else [rowEnableTouchDaceID setValue:@0]; diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermission.swift b/iOSClient/Share/Advanced/NCShareAdvancePermission.swift new file mode 100644 index 000000000..2e0da22c0 --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareAdvancePermission.swift @@ -0,0 +1,192 @@ +// +// NCShareAdvancePermission.swift +// Nextcloud +// +// Created by T-systems on 09/08/21. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 +import SVGKit +import CloudKit + +class NCShareAdvancePermission: UITableViewController, NCShareAdvanceFotterDelegate, NCShareDetail { + func dismissShareAdvanceView(shouldSave: Bool) { + guard shouldSave else { + guard oldTableShare?.hasChanges(comparedTo: share) != false else { + navigationController?.popViewController(animated: true) + return + } + let alert = UIAlertController( + title: NSLocalizedString("_cancel_request_", comment: ""), + message: NSLocalizedString("_discard_changes_info_", comment: ""), + preferredStyle: .alert) + alert.addAction(UIAlertAction( + title: NSLocalizedString("_discard_changes_", comment: ""), + style: .destructive, + handler: { _ in self.navigationController?.popViewController(animated: true) })) + alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_editing_", comment: ""), style: .default)) + self.present(alert, animated: true) + return + } + if isNewShare { + networking?.createShare(option: share) + } else { + networking?.updateShare(option: share) + } + navigationController?.popViewController(animated: true) + } + + var oldTableShare: tableShare? + var share: NCTableShareable! + var isNewShare: Bool { share is NCTableShareOptions } + var metadata: tableMetadata! + var shareConfig: NCShareConfig! + var networking: NCShareNetworking? + + override func viewDidLoad() { + super.viewDidLoad() + self.shareConfig = NCShareConfig(parentMetadata: metadata, share: share) + + tableView.estimatedRowHeight = tableView.rowHeight + tableView.rowHeight = UITableView.automaticDimension + self.setNavigationTitle() + self.navigationItem.hidesBackButton = true + if #available(iOS 13.0, *) { + // disbale pull to dimiss + isModalInPresentation = true + } + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + guard tableView.tableHeaderView == nil, tableView.tableFooterView == nil else { return } + setupHeaderView() + setupFooterView() + } + + func setupFooterView() { + guard let footerView = (Bundle.main.loadNibNamed("NCShareAdvancePermissionFooter", owner: self, options: nil)?.first as? NCShareAdvancePermissionFooter) else { return } + footerView.setupUI(delegate: self) + + // tableFooterView can't use auto layout directly + let container = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 120)) + container.addSubview(footerView) + tableView.tableFooterView = container + footerView.translatesAutoresizingMaskIntoConstraints = false + footerView.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true + footerView.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true + footerView.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true + } + + func setupHeaderView() { + guard let headerView = (Bundle.main.loadNibNamed("NCShareAdvancePermissionHeader", owner: self, options: nil)?.first as? NCShareAdvancePermissionHeader) else { return } + headerView.setupUI(with: metadata) + + let container = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 220)) + container.addSubview(headerView) + tableView.tableHeaderView = container + headerView.translatesAutoresizingMaskIntoConstraints = false + headerView.topAnchor.constraint(equalTo: container.topAnchor).isActive = true + headerView.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true + headerView.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + if section == 0 { + return NSLocalizedString("_permissions_", comment: "") + } else if section == 1 { + return NSLocalizedString("_advanced_", comment: "") + } else { return nil } + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 2 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if section == 0 { + // check reshare permission, if restricted add note + let maxPermission = metadata.directory ? NCGlobal.shared.permissionMaxFolderShare : NCGlobal.shared.permissionMaxFileShare + return shareConfig.resharePermission != maxPermission ? shareConfig.permissions.count + 1 : shareConfig.permissions.count + } else if section == 1 { + return shareConfig.advanced.count + } else { return 0 } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = shareConfig.cellFor(indexPath: indexPath) else { + let noteCell = UITableViewCell(style: .subtitle, reuseIdentifier: "noteCell") + noteCell.detailTextLabel?.text = NSLocalizedString("_share_reshare_restricted_", comment: "") + noteCell.detailTextLabel?.isEnabled = false + noteCell.isUserInteractionEnabled = false + noteCell.detailTextLabel?.numberOfLines = 0 + return noteCell + } + if let cell = cell as? NCShareDateCell { cell.onReload = tableView.reloadData } + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + guard let cellConfig = shareConfig.config(for: indexPath) else { return } + guard let cellConfig = cellConfig as? NCShareDetails else { + cellConfig.didSelect(for: share) + tableView.reloadData() + return + } + + switch cellConfig { + case .hideDownload: + share.hideDownload.toggle() + tableView.reloadData() + case .expirationDate: + let cell = tableView.cellForRow(at: indexPath) as? NCShareDateCell + cell?.textField.becomeFirstResponder() + case .password: + guard share.password.isEmpty else { + share.password = "" + tableView.reloadData() + return + } + let alertController = UIAlertController.password(titleKey: "_share_password_") { password in + self.share.password = password ?? "" + tableView.reloadData() + } + self.present(alertController, animated: true) + case .note: + let storyboard = UIStoryboard(name: "NCShare", bundle: nil) + guard let viewNewUserComment = storyboard.instantiateViewController(withIdentifier: "NCShareNewUserAddComment") as? NCShareNewUserAddComment else { return } + viewNewUserComment.metadata = self.metadata + viewNewUserComment.share = self.share + viewNewUserComment.onDismiss = tableView.reloadData + self.navigationController?.pushViewController(viewNewUserComment, animated: true) + case .label: + let alertController = UIAlertController.withTextField(titleKey: "_share_link_name_") { textField in + textField.placeholder = cellConfig.title + textField.text = self.share.label + } completion: { newValue in + self.share.label = newValue ?? "" + self.setNavigationTitle() + tableView.reloadData() + } + self.present(alertController, animated: true) + } + } +} diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift new file mode 100644 index 000000000..ffad1f97e --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift @@ -0,0 +1,66 @@ +// +// NCShareAdvancePermissionFooter.swift +// Nextcloud +// +// Created by T-systems on 09/08/21. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +protocol NCShareAdvanceFotterDelegate: AnyObject { + var isNewShare: Bool { get } + func dismissShareAdvanceView(shouldSave: Bool) +} + +class NCShareAdvancePermissionFooter: UIView { + @IBOutlet weak var buttonCancel: UIButton! + @IBOutlet weak var buttonNext: UIButton! + weak var delegate: NCShareAdvanceFotterDelegate? + + func setupUI(delegate: NCShareAdvanceFotterDelegate?) { + 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) + + 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) + } + + @objc func cancelClicked() { + delegate?.dismissShareAdvanceView(shouldSave: false) + } + + @objc func nextClicked() { + delegate?.dismissShareAdvanceView(shouldSave: true) + } +} diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.xib b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.xib new file mode 100644 index 000000000..40b515e7d --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.xib @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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="retina6_1" 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> + <objects> + <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> + <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> + <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareAdvancePermissionFooter" customModule="Nextcloud" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="688" height="376"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rCI-63-PtL"> + <rect key="frame" x="16" y="168" width="320" height="50"/> + <constraints> + <constraint firstAttribute="height" constant="50" id="vdI-sH-cTb"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> + <state key="normal" title="Cancel"> + <color key="titleColor" systemColor="labelColor"/> + </state> + </button> + <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mg2-Ou-yoX"> + <rect key="frame" x="352" y="168" width="320" height="50"/> + <color key="backgroundColor" systemColor="systemBlueColor"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <state key="normal" title="Send share"> + <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </state> + </button> + </subviews> + <viewLayoutGuide key="safeArea" id="sWQ-1v-CIt"/> + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="Mg2-Ou-yoX" firstAttribute="leading" secondItem="rCI-63-PtL" secondAttribute="trailing" constant="16" id="4Px-cS-Hta"/> + <constraint firstItem="sWQ-1v-CIt" firstAttribute="trailing" secondItem="Mg2-Ou-yoX" secondAttribute="trailing" constant="16" id="Jdi-WJ-zVF"/> + <constraint firstItem="sWQ-1v-CIt" firstAttribute="centerY" secondItem="Mg2-Ou-yoX" secondAttribute="centerY" id="SzT-aI-aUz"/> + <constraint firstItem="Mg2-Ou-yoX" firstAttribute="height" secondItem="rCI-63-PtL" secondAttribute="height" id="bNp-Nf-uMw"/> + <constraint firstItem="Mg2-Ou-yoX" firstAttribute="width" secondItem="rCI-63-PtL" secondAttribute="width" id="dfu-GZ-P99"/> + <constraint firstItem="rCI-63-PtL" firstAttribute="centerY" secondItem="Mg2-Ou-yoX" secondAttribute="centerY" id="hEl-ij-sMX"/> + <constraint firstItem="rCI-63-PtL" firstAttribute="leading" secondItem="sWQ-1v-CIt" secondAttribute="leading" constant="16" id="s9t-ud-ofw"/> + </constraints> + <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> + <connections> + <outlet property="buttonCancel" destination="rCI-63-PtL" id="SoT-Ko-LL5"/> + <outlet property="buttonNext" destination="Mg2-Ou-yoX" id="F2d-L5-dHo"/> + </connections> + <point key="canvasLocation" x="139.13043478260872" y="43.526785714285715"/> + </view> + </objects> + <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> + </resources> +</document> diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.swift b/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.swift new file mode 100644 index 000000000..8ff5828b7 --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.swift @@ -0,0 +1,51 @@ +// +// NCShareAdvancePermissionHeader.swift +// Nextcloud +// +// Created by T-systems on 10/08/21. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 NCShareAdvancePermissionHeader: UIView { + @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var fileName: UILabel! + @IBOutlet weak var info: UILabel! + @IBOutlet weak var fullWidthImageView: UIImageView! + + func setupUI(with metadata: tableMetadata) { + if FileManager.default.fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) { + fullWidthImageView.image = NCUtility.shared.getImageMetadata(metadata, for: frame.height) + fullWidthImageView.contentMode = .scaleAspectFill + imageView.isHidden = true + } else { + if metadata.directory { + imageView.image = NCBrandColor.cacheImages.folder + } else if !metadata.iconName.isEmpty { + imageView.image = UIImage(named: metadata.iconName) + } else { + imageView.image = NCBrandColor.cacheImages.file + } + } + fileName.text = metadata.fileNameView + fileName.textColor = NCBrandColor.shared.label + info.textColor = NCBrandColor.shared.secondaryLabel + info.text = CCUtility.transformedSize(metadata.size) + ", " + CCUtility.dateDiff(metadata.date as Date) + } +} diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.xib b/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.xib new file mode 100644 index 000000000..beb60d8c5 --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareAdvancePermissionHeader.xib @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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="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"/> + <view contentMode="scaleToFill" id="wbW-yR-MZC" customClass="NCShareAdvancePermissionHeader" customModule="Nextcloud" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="414" height="210"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cki-Ql-o1I"> + <rect key="frame" x="0.0" y="0.0" width="414" height="150"/> + <constraints> + <constraint firstAttribute="height" constant="150" id="9GH-KF-f8P"/> + </constraints> + </imageView> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Dkw-QP-be6"> + <rect key="frame" x="16" y="27" width="96" height="96"/> + <constraints> + <constraint firstAttribute="height" constant="96" id="TW7-8v-NRJ"/> + <constraint firstAttribute="width" constant="96" id="Vbz-xZ-L5k"/> + </constraints> + </imageView> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pu4-Nd-M08"> + <rect key="frame" x="16" y="166" width="382" height="18"/> + <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VaC-Fz-kHk"> + <rect key="frame" x="16" y="188" width="383" height="16"/> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <color key="textColor" red="0.76862745099999996" green="0.77647058820000003" blue="0.77647058820000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <viewLayoutGuide key="safeArea" id="KLE-vd-Dsn"/> + <constraints> + <constraint firstItem="pu4-Nd-M08" firstAttribute="leading" secondItem="KLE-vd-Dsn" secondAttribute="leading" constant="16" id="0Uy-ra-MtM"/> + <constraint firstItem="pu4-Nd-M08" firstAttribute="top" secondItem="cki-Ql-o1I" secondAttribute="bottom" constant="16" id="Cce-7Y-iw9"/> + <constraint firstAttribute="trailing" secondItem="VaC-Fz-kHk" secondAttribute="trailing" constant="15" id="Ebg-3T-03W"/> + <constraint firstItem="Dkw-QP-be6" firstAttribute="leading" secondItem="KLE-vd-Dsn" secondAttribute="leading" constant="16" id="KPc-al-RWN"/> + <constraint firstItem="KLE-vd-Dsn" firstAttribute="top" secondItem="cki-Ql-o1I" secondAttribute="top" id="KsC-jf-M6F"/> + <constraint firstAttribute="trailing" secondItem="cki-Ql-o1I" secondAttribute="trailing" id="Wdl-Rb-Pjy"/> + <constraint firstItem="VaC-Fz-kHk" firstAttribute="leading" secondItem="pu4-Nd-M08" secondAttribute="leading" id="Ycb-wy-uL6"/> + <constraint firstItem="cki-Ql-o1I" firstAttribute="leading" secondItem="KLE-vd-Dsn" secondAttribute="leading" id="ZlH-LU-x2x"/> + <constraint firstItem="KLE-vd-Dsn" firstAttribute="trailing" secondItem="pu4-Nd-M08" secondAttribute="trailing" constant="16" id="bO0-yQ-OWX"/> + <constraint firstItem="Dkw-QP-be6" firstAttribute="centerY" secondItem="cki-Ql-o1I" secondAttribute="centerY" id="dME-jJ-PwO"/> + <constraint firstItem="VaC-Fz-kHk" firstAttribute="top" secondItem="pu4-Nd-M08" secondAttribute="bottom" constant="4" id="zkb-Jm-sin"/> + </constraints> + <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> + <connections> + <outlet property="fileName" destination="pu4-Nd-M08" id="iLj-ya-6hM"/> + <outlet property="fullWidthImageView" destination="cki-Ql-o1I" id="KYn-3Y-SZH"/> + <outlet property="imageView" destination="Dkw-QP-be6" id="wzX-Sb-Ajt"/> + <outlet property="info" destination="VaC-Fz-kHk" id="n9O-6c-qsd"/> + </connections> + <point key="canvasLocation" x="35.625" y="27.5"/> + </view> + </objects> +</document> diff --git a/iOSClient/Share/Advanced/NCShareCells.swift b/iOSClient/Share/Advanced/NCShareCells.swift new file mode 100644 index 000000000..4806f6350 --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareCells.swift @@ -0,0 +1,304 @@ +// +// NCShareCells.swift +// Nextcloud +// +// Created by Henrik Storch on 18.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +protocol NCShareCellConfig { + var title: String { get } + func getCell(for share: NCTableShareable) -> UITableViewCell + func didSelect(for share: NCTableShareable) +} + +protocol NCToggleCellConfig: NCShareCellConfig { + func isOn(for share: NCTableShareable) -> Bool + func didChange(_ share: NCTableShareable, to newValue: Bool) +} + +extension NCToggleCellConfig { + func getCell(for share: NCTableShareable) -> UITableViewCell { + return NCShareToggleCell(isOn: isOn(for: share)) + } + + func didSelect(for share: NCTableShareable) { + didChange(share, to: !isOn(for: share)) + } +} + +protocol NCPermission: NCToggleCellConfig { + static var forDirectory: [Self] { get } + static var forFile: [Self] { get } + func hasResharePermission(for parentPermission: Int) -> Bool +} + +enum NCUserPermission: CaseIterable, NCPermission { + func hasResharePermission(for parentPermission: Int) -> Bool { + return ((permissionBitFlag & parentPermission) != 0) + } + + var permissionBitFlag: Int { + switch self { + case .reshare: return NCGlobal.shared.permissionShareShare + case .edit: return NCGlobal.shared.permissionUpdateShare + case .create: return NCGlobal.shared.permissionCreateShare + case .delete: return NCGlobal.shared.permissionDeleteShare + } + } + + func didChange(_ share: NCTableShareable, to newValue: Bool) { + share.permissions ^= permissionBitFlag + } + + func isOn(for share: NCTableShareable) -> Bool { + return (share.permissions & permissionBitFlag) != 0 + } + + case reshare, edit, create, delete + static let forDirectory: [NCUserPermission] = NCUserPermission.allCases + static let forFile: [NCUserPermission] = [.reshare, .edit] + + var title: String { + switch self { + case .reshare: return NSLocalizedString("_share_can_reshare_", comment: "") + case .edit: return NSLocalizedString("_share_can_change_", comment: "") + case .create: return NSLocalizedString("_share_can_create_", comment: "") + case .delete: return NSLocalizedString("_share_can_delete_", comment: "") + } + } +} + +enum NCLinkPermission: NCPermission { + func didChange(_ share: NCTableShareable, to newValue: Bool) { + guard self != .allowEdit || newValue else { + share.permissions = NCGlobal.shared.permissionReadShare + return + } + share.permissions = permissionValue + } + + func hasResharePermission(for parentPermission: Int) -> Bool { + permissionValue & parentPermission == permissionValue + } + + var permissionValue: Int { + switch self { + case .allowEdit: + return CCUtility.getPermissionsValue( + byCanEdit: true, + andCanCreate: true, + andCanChange: true, + andCanDelete: true, + andCanShare: false, + andIsFolder: false) + case .viewOnly: + return CCUtility.getPermissionsValue( + byCanEdit: false, + andCanCreate: false, + andCanChange: false, + andCanDelete: false, + // not possible to create "read-only" shares without reshare option + // https://github.com/nextcloud/server/blame/f99876997a9119518fe5f7ad3a3a51d33459d4cc/apps/files_sharing/lib/Controller/ShareAPIController.php#L1104-L1107 + andCanShare: true, + andIsFolder: true) + case .uploadEdit: + return CCUtility.getPermissionsValue( + byCanEdit: true, + andCanCreate: true, + andCanChange: true, + andCanDelete: true, + andCanShare: false, + andIsFolder: true) + case .fileDrop: + return NCGlobal.shared.permissionCreateShare + } + } + + func isOn(for share: NCTableShareable) -> Bool { + switch self { + case .allowEdit: return CCUtility.isAnyPermission(toEdit: share.permissions) + case .viewOnly: return !CCUtility.isAnyPermission(toEdit: share.permissions) && share.permissions != NCGlobal.shared.permissionCreateShare + case .uploadEdit: return CCUtility.isAnyPermission(toEdit: share.permissions) && share.permissions != NCGlobal.shared.permissionCreateShare + case .fileDrop: return share.permissions == NCGlobal.shared.permissionCreateShare + } + } + + var title: String { + switch self { + case .allowEdit: return NSLocalizedString("_share_can_change_", comment: "") + case .viewOnly: return NSLocalizedString("_share_read_only_", comment: "") + case .uploadEdit: return NSLocalizedString("_share_allow_upload_", comment: "") + case .fileDrop: return NSLocalizedString("_share_file_drop_", comment: "") + } + } + + case allowEdit, viewOnly, uploadEdit, fileDrop + static let forDirectory: [NCLinkPermission] = [.viewOnly, .uploadEdit, .fileDrop] + static let forFile: [NCLinkPermission] = [.allowEdit] +} + +enum NCShareDetails: CaseIterable, NCShareCellConfig { + func didSelect(for share: NCTableShareable) { + switch self { + case .hideDownload: share.hideDownload.toggle() + case .expirationDate: return + case .password: return + case .note: return + case .label: return + } + } + + func getCell(for share: NCTableShareable) -> UITableViewCell { + switch self { + case .hideDownload: + return NCShareToggleCell(isOn: share.hideDownload) + case .expirationDate: + return NCShareDateCell(share: share) + case .password: return NCShareToggleCell(isOn: !share.password.isEmpty, customIcons: ("lock", "lock_open")) + case .note: + let cell = UITableViewCell(style: .value1, reuseIdentifier: "shareNote") + cell.detailTextLabel?.text = share.note + cell.accessoryType = .disclosureIndicator + return cell + case .label: + let cell = UITableViewCell(style: .value1, reuseIdentifier: "shareLabel") + cell.detailTextLabel?.text = share.label + return cell + } + } + + var title: String { + switch self { + case .hideDownload: return NSLocalizedString("_share_hide_download_", comment: "") + case .expirationDate: return NSLocalizedString("_share_expiration_date_", comment: "") + case .password: return NSLocalizedString("_share_password_protect_", comment: "") + case .note: return NSLocalizedString("_share_note_recipient_", comment: "") + case .label: return NSLocalizedString("_share_link_name_", comment: "") + } + } + + case label, hideDownload, expirationDate, password, note + static let forLink: [NCShareDetails] = NCShareDetails.allCases + static let forUser: [NCShareDetails] = [.expirationDate, .note] +} + +struct NCShareConfig { + let permissions: [NCPermission] + let advanced: [NCShareDetails] + let share: NCTableShareable + let resharePermission: Int + + init(parentMetadata: tableMetadata, share: NCTableShareable) { + self.share = share + self.resharePermission = parentMetadata.sharePermissionsCollaborationServices + let type: NCPermission.Type = share.shareType == NCShareCommon.shared.SHARE_TYPE_LINK ? NCLinkPermission.self : NCUserPermission.self + self.permissions = parentMetadata.directory ? type.forDirectory : type.forFile + self.advanced = share.shareType == NCShareCommon.shared.SHARE_TYPE_LINK ? NCShareDetails.forLink : NCShareDetails.forUser + } + + func cellFor(indexPath: IndexPath) -> UITableViewCell? { + let cellConfig = config(for: indexPath) + let cell = cellConfig?.getCell(for: share) + cell?.textLabel?.text = cellConfig?.title + if let cellConfig = cellConfig as? NCPermission, !cellConfig.hasResharePermission(for: resharePermission) { + cell?.isUserInteractionEnabled = false + cell?.textLabel?.isEnabled = false + } + return cell + } + + func didSelectRow(at indexPath: IndexPath) { + let cellConfig = config(for: indexPath) + cellConfig?.didSelect(for: share) + } + + func config(for indexPath: IndexPath) -> NCShareCellConfig? { + if indexPath.section == 0, indexPath.row < permissions.count { + return permissions[indexPath.row] + } else if indexPath.section == 1, indexPath.row < advanced.count { + return advanced[indexPath.row] + } else { return nil } + } +} + +class NCShareToggleCell: UITableViewCell { + typealias CustomToggleIcon = (onIconName: String?, offIconName: String?) + init(isOn: Bool, customIcons: CustomToggleIcon? = nil) { + super.init(style: .default, reuseIdentifier: "toggleCell") + self.accessibilityValue = isOn ? NSLocalizedString("_on_", comment: "") : NSLocalizedString("_off_", comment: "") + + guard let customIcons = customIcons, + let iconName = isOn ? customIcons.onIconName : customIcons.offIconName else { + self.accessoryType = isOn ? .checkmark : .none + return + } + let image = NCUtility.shared.loadImage(named: iconName, color: NCBrandColor.shared.brandElement, size: self.frame.height - 26) + self.accessoryView = UIImageView(image: image) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +class NCShareDateCell: UITableViewCell { + let picker = UIDatePicker() + let textField = UITextField() + + var onReload: (() -> Void)? + + init(share: NCTableShareable) { + super.init(style: .value1, reuseIdentifier: "shareExpDate") + picker.datePickerMode = .date + picker.minimumDate = Date() + if #available(iOS 13.4, *) { + picker.preferredDatePickerStyle = .wheels + } + picker.action(for: .valueChanged) { datePicker in + guard let datePicker = datePicker as? UIDatePicker else { return } + self.detailTextLabel?.text = DateFormatter.shareExpDate.string(from: datePicker.date) + } + accessoryView = textField + + let toolbar = UIToolbar.toolbar { + self.resignFirstResponder() + share.expirationDate = nil + self.onReload?() + } completion: { + self.resignFirstResponder() + share.expirationDate = self.picker.date as NSDate + self.onReload?() + } + + textField.isAccessibilityElement = false + textField.accessibilityElementsHidden = true + textField.inputAccessoryView = toolbar.wrappedSafeAreaContainer + textField.inputView = picker + + if let expDate = share.expirationDate { + detailTextLabel?.text = DateFormatter.shareExpDate.string(from: expDate as Date) + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/iOSClient/Share/Advanced/NCShareNewUserAddComment.swift b/iOSClient/Share/Advanced/NCShareNewUserAddComment.swift new file mode 100644 index 000000000..73af68588 --- /dev/null +++ b/iOSClient/Share/Advanced/NCShareNewUserAddComment.swift @@ -0,0 +1,95 @@ +// +// NCShareNewUserAddComment.swift +// Nextcloud +// +// Created by TSI-mc on 21/06/21. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 +import SVGKit + +class NCShareNewUserAddComment: UIViewController, NCShareDetail { + + @IBOutlet weak var headerContainerView: UIView! + @IBOutlet weak var sharingLabel: UILabel! + @IBOutlet weak var noteTextField: UITextView! + + let contentInsets: CGFloat = 16 + var onDismiss: (() -> Void)? + + public var share: NCTableShareable! + public var metadata: tableMetadata! + + override func viewDidLoad() { + super.viewDidLoad() + self.setNavigationTitle() + + NotificationCenter.default.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(adjustForKeyboard), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) + + sharingLabel.text = NSLocalizedString("_share_note_recipient_", comment: "") + + noteTextField.textContainerInset = UIEdgeInsets(top: contentInsets, left: contentInsets, bottom: contentInsets, right: contentInsets) + noteTextField.text = share.note + let toolbar = UIToolbar.toolbar { + self.noteTextField.resignFirstResponder() + self.noteTextField.text = "" + self.share.note = "" + } completion: { + self.noteTextField.resignFirstResponder() + self.share.note = self.noteTextField.text + } + + noteTextField.inputAccessoryView = toolbar.wrappedSafeAreaContainer + + guard let headerView = (Bundle.main.loadNibNamed("NCShareAdvancePermissionHeader", owner: self, options: nil)?.first as? NCShareAdvancePermissionHeader) else { return } + headerContainerView.addSubview(headerView) + headerView.frame = headerContainerView.frame + headerView.translatesAutoresizingMaskIntoConstraints = false + headerView.topAnchor.constraint(equalTo: headerContainerView.topAnchor).isActive = true + headerView.bottomAnchor.constraint(equalTo: headerContainerView.bottomAnchor).isActive = true + headerView.leftAnchor.constraint(equalTo: headerContainerView.leftAnchor).isActive = true + headerView.rightAnchor.constraint(equalTo: headerContainerView.rightAnchor).isActive = true + + headerView.setupUI(with: metadata) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + share.note = noteTextField.text + onDismiss?() + } + + @objc func adjustForKeyboard(notification: Notification) { + guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue, + let globalTextViewFrame = noteTextField.superview?.convert(noteTextField.frame, to: nil) else { return } + + let keyboardScreenEndFrame = keyboardValue.cgRectValue + let portionCovoredByLeyboard = globalTextViewFrame.maxY - keyboardScreenEndFrame.minY + + if notification.name == UIResponder.keyboardWillHideNotification || portionCovoredByLeyboard < 0 { + noteTextField.contentInset = .zero + } else { + noteTextField.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: portionCovoredByLeyboard, right: 0) + } + + noteTextField.scrollIndicatorInsets = noteTextField.contentInset + } +} diff --git a/iOSClient/Share/NCShareUserDropDownCell.xib b/iOSClient/Share/NCSearchUserDropDownCell.xib index 9701c08c8..fa63960bc 100755 --- a/iOSClient/Share/NCShareUserDropDownCell.xib +++ b/iOSClient/Share/NCSearchUserDropDownCell.xib @@ -1,15 +1,15 @@ <?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" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="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="19454"/> <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"/> - <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareUserDropDownCell" customModule="Nextcloud" customModuleProvider="target"> + <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCSearchUserDropDownCell" customModule="Nextcloud" customModuleProvider="target"> <rect key="frame" x="0.0" y="0.0" width="487" height="50"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> diff --git a/iOSClient/Share/NCShare+Helper.swift b/iOSClient/Share/NCShare+Helper.swift new file mode 100644 index 000000000..bb108652c --- /dev/null +++ b/iOSClient/Share/NCShare+Helper.swift @@ -0,0 +1,109 @@ +// +// NCShare+Helper.swift +// Nextcloud +// +// Created by Henrik Storch on 19.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +extension tableShare: NCTableShareable { } +extension NCCommunicationShare: NCTableShareable { } + +protocol NCTableShareable: AnyObject { + var shareType: Int { get set } + var permissions: Int { get set } + + var idShare: Int { get set } + var shareWith: String { get set } + + var hideDownload: Bool { get set } + var password: String { get set } + var label: String { get set } + var note: String { get set } + var expirationDate: NSDate? { get set } + var shareWithDisplayname: String { get set } +} + +extension NCTableShareable { + var expDateString: String? { + guard let date = expirationDate else { return nil } + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss" + return dateFormatter.string(from: date as Date) + } + + func hasChanges(comparedTo other: NCTableShareable) -> Bool { + return other.shareType != shareType + || other.permissions != permissions + || other.hideDownload != hideDownload + || other.password != password + || other.label != label + || other.note != note + || other.expirationDate != expirationDate + } +} + +class NCTableShareOptions: NCTableShareable { + var shareType: Int + var permissions: Int + + var idShare: Int = 0 + var shareWith: String = "" + + var hideDownload: Bool = false + var password: String = "" + var label: String = "" + var note: String = "" + var expirationDate: NSDate? + var shareWithDisplayname: String = "" + + private init(shareType: Int, metadata: tableMetadata, password: String?) { + self.permissions = NCManageDatabase.shared.getCapabilitiesServerInt(account: metadata.account, elements: ["ocs", "data", "capabilities", "files_sharing", "default_permissions"]) & metadata.sharePermissionsCollaborationServices + self.shareType = shareType + if let password = password { + self.password = password + } + } + + convenience init(sharee: NCCommunicationSharee, metadata: tableMetadata, password: String?) { + self.init(shareType: sharee.shareType, metadata: metadata, password: password) + self.shareWith = sharee.shareWith + } + + static func shareLink(metadata: tableMetadata, password: String?) -> NCTableShareOptions { + return NCTableShareOptions(shareType: NCShareCommon.shared.SHARE_TYPE_LINK, metadata: metadata, password: password) + } +} + +protocol NCShareDetail { + var share: NCTableShareable! { get } +} + +extension NCShareDetail where Self: UIViewController { + func setNavigationTitle() { + title = NSLocalizedString("_share_", comment: "") + " – " + if share.shareType == NCShareCommon.shared.SHARE_TYPE_LINK { + title! += share.label.isEmpty ? NSLocalizedString("_share_link_", comment: "") : share.label + } else { + title! += share.shareWithDisplayname.isEmpty ? share.shareWith : share.shareWithDisplayname + } + } +} diff --git a/iOSClient/Share/NCShare+NCCellDelegate.swift b/iOSClient/Share/NCShare+NCCellDelegate.swift new file mode 100644 index 000000000..78dfd1074 --- /dev/null +++ b/iOSClient/Share/NCShare+NCCellDelegate.swift @@ -0,0 +1,69 @@ +// +// NCShare+NCCellDelegate.swift +// Nextcloud +// +// Created by Henrik Storch on 03.01.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 + +// MARK: - NCCell Delegates +extension NCShare: NCShareLinkCellDelegate, NCShareUserCellDelegate { + + func copyInternalLink(sender: Any) { + 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 + if errorCode == 0, let metadata = metadata { + let internalLink = appDelegate.urlBase + "/index.php/f/" + metadata.fileId + NCShareCommon.shared.copyLink(link: internalLink, viewController: self, sender: sender) + } else { + NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } + } + } + + func tapCopy(with tableShare: tableShare?, sender: Any) { + guard let tableShare = tableShare else { + return copyInternalLink(sender: sender) + } + NCShareCommon.shared.copyLink(link: tableShare.url, viewController: self, sender: sender) + } + + func tapMenu(with tableShare: tableShare?, sender: Any) { + if let tableShare = tableShare { + self.toggleShareMenu(for: tableShare) + } else { + self.makeNewLinkShare() + } + } + + func showProfile(with tableShare: tableShare?, sender: Any) { + guard let tableShare = tableShare else { return } + showProfileMenu(userId: tableShare.shareWith) + } + + func quickStatus(with tableShare: tableShare?, sender: Any) { + guard let tableShare = tableShare, + let metadata = metadata, + tableShare.shareType != NCGlobal.shared.permissionDefaultFileRemoteShareNoSupportShareOption else { return } + self.toggleUserPermissionMenu(isDirectory: metadata.directory, tableShare: tableShare) + } +} diff --git a/iOSClient/Share/NCShare.storyboard b/iOSClient/Share/NCShare.storyboard index 23d454487..05040c5e5 100644 --- a/iOSClient/Share/NCShare.storyboard +++ b/iOSClient/Share/NCShare.storyboard @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ts3-RO-A9l"> +<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="Ts3-RO-A9l"> <device id="retina5_5" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> + <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> @@ -54,66 +55,9 @@ <rect key="frame" x="5" y="0.0" width="404" height="726"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="c94-b9-Sim"> - <rect key="frame" x="0.0" y="250" width="404" height="476"/> + <rect key="frame" x="0.0" y="133" width="404" height="593"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </tableView> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="8Cj-cK-AKZ"> - <rect key="frame" x="5" y="139" width="40" height="40"/> - <constraints> - <constraint firstAttribute="height" constant="40" id="CCv-Uu-Vel"/> - <constraint firstAttribute="width" constant="40" id="egJ-xl-yj4"/> - </constraints> - </imageView> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HPl-mj-r5E"> - <rect key="frame" x="5" y="199" width="40" height="40"/> - <constraints> - <constraint firstAttribute="width" constant="40" id="6IE-lI-M6i"/> - <constraint firstAttribute="height" constant="40" id="Odq-bX-Hoz"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Share link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SQW-aQ-ydN"> - <rect key="frame" x="53" y="150" width="261" height="18"/> - <fontDescription key="fontDescription" type="system" pointSize="15"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Share link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YYp-o8-YJP"> - <rect key="frame" x="53" y="195" width="317" height="18"/> - <fontDescription key="fontDescription" type="system" pointSize="15"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pfo-D0-W7b"> - <rect key="frame" x="53" y="216.66666666666666" width="317" height="35"/> - <constraints> - <constraint firstAttribute="height" constant="35" id="f8b-mp-xLJ"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Qek-aQ-NjE" userLabel="ButtonMenu"> - <rect key="frame" x="374" y="149" width="20" height="20"/> - <constraints> - <constraint firstAttribute="width" constant="20" id="BAT-jK-rUt"/> - <constraint firstAttribute="height" constant="20" id="zc5-W6-SXG"/> - </constraints> - <state key="normal" image="shareMenu"/> - <connections> - <action selector="touchUpInsideButtonMenu:" destination="bgO-Rz-2M1" eventType="touchUpInside" id="ogE-7H-hMG"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cLd-wD-cSC" userLabel="ButtonCopy"> - <rect key="frame" x="324" y="149" width="20" height="20"/> - <constraints> - <constraint firstAttribute="width" constant="20" id="Bzl-zW-yzd"/> - <constraint firstAttribute="height" constant="20" id="RIV-EC-kwC"/> - </constraints> - <state key="normal" image="shareCopy"/> - <connections> - <action selector="touchUpInsideButtonCopy:" destination="bgO-Rz-2M1" eventType="touchUpInside" id="ccu-6N-Tm4"/> - </connections> - </button> <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oBQ-TP-qof" userLabel="View Shared with you by"> <rect key="frame" x="-5" y="10" width="409" height="90"/> <subviews> @@ -172,46 +116,17 @@ <action selector="searchFieldDidEndOnExitWithTextField:" destination="bgO-Rz-2M1" eventType="editingDidEndOnExit" id="xH6-YR-5W9"/> </connections> </textField> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FFi-7t-C8U" userLabel="ButtonCopy"> - <rect key="frame" x="375" y="209" width="20" height="20"/> - <constraints> - <constraint firstAttribute="height" constant="20" id="0KI-54-GMc"/> - <constraint firstAttribute="width" constant="20" id="fcI-Wc-4GE"/> - </constraints> - <state key="normal" image="shareCopy"/> - <connections> - <action selector="touchUpInsideButtonCopyInernalLink:" destination="bgO-Rz-2M1" eventType="touchUpInside" id="fmb-gx-9PH"/> - </connections> - </button> </subviews> <constraints> <constraint firstItem="oBQ-TP-qof" firstAttribute="top" secondItem="X2m-IC-J1u" secondAttribute="top" constant="10" id="09Y-bm-RvQ"/> - <constraint firstItem="HPl-mj-r5E" firstAttribute="top" secondItem="8Cj-cK-AKZ" secondAttribute="bottom" constant="20" id="0dX-Ni-vDj"/> <constraint firstAttribute="trailing" secondItem="c94-b9-Sim" secondAttribute="trailing" id="BtN-cJ-TTc"/> - <constraint firstItem="c94-b9-Sim" firstAttribute="top" secondItem="iSO-mc-0TB" secondAttribute="bottom" constant="125" id="Co6-l6-HiT"/> - <constraint firstItem="FFi-7t-C8U" firstAttribute="leading" secondItem="YYp-o8-YJP" secondAttribute="trailing" constant="5" id="IE6-R3-yOv"/> - <constraint firstItem="SQW-aQ-ydN" firstAttribute="centerY" secondItem="8Cj-cK-AKZ" secondAttribute="centerY" id="LtS-8d-L7a"/> - <constraint firstItem="Qek-aQ-NjE" firstAttribute="centerY" secondItem="8Cj-cK-AKZ" secondAttribute="centerY" id="NYZ-hc-SBk"/> - <constraint firstItem="SQW-aQ-ydN" firstAttribute="leading" secondItem="8Cj-cK-AKZ" secondAttribute="trailing" constant="8" id="Oby-Ea-MaC"/> - <constraint firstItem="cLd-wD-cSC" firstAttribute="leading" secondItem="SQW-aQ-ydN" secondAttribute="trailing" constant="10" id="PFh-qU-yXY"/> - <constraint firstItem="YYp-o8-YJP" firstAttribute="leading" secondItem="HPl-mj-r5E" secondAttribute="trailing" constant="8" id="R36-FW-w0B"/> - <constraint firstItem="pfo-D0-W7b" firstAttribute="centerY" secondItem="HPl-mj-r5E" secondAttribute="centerY" constant="15" id="Rji-xW-vn7"/> + <constraint firstItem="c94-b9-Sim" firstAttribute="top" secondItem="iSO-mc-0TB" secondAttribute="bottom" constant="8" id="Co6-l6-HiT"/> <constraint firstAttribute="bottom" secondItem="c94-b9-Sim" secondAttribute="bottom" id="Svm-RV-vnl"/> <constraint firstAttribute="trailing" secondItem="iSO-mc-0TB" secondAttribute="trailing" constant="10" id="Vhu-GP-EJN"/> - <constraint firstItem="8Cj-cK-AKZ" firstAttribute="leading" secondItem="X2m-IC-J1u" secondAttribute="leading" constant="5" id="WlZ-CY-x4s"/> <constraint firstAttribute="trailing" secondItem="oBQ-TP-qof" secondAttribute="trailing" id="ZuM-2G-aoM"/> - <constraint firstItem="Qek-aQ-NjE" firstAttribute="leading" secondItem="cLd-wD-cSC" secondAttribute="trailing" constant="30" id="bSw-vM-d12"/> <constraint firstItem="iSO-mc-0TB" firstAttribute="leading" secondItem="X2m-IC-J1u" secondAttribute="leading" constant="5" id="d8E-WM-YfC"/> - <constraint firstItem="FFi-7t-C8U" firstAttribute="centerY" secondItem="HPl-mj-r5E" secondAttribute="centerY" id="fkL-uP-Iob"/> - <constraint firstItem="YYp-o8-YJP" firstAttribute="centerY" secondItem="HPl-mj-r5E" secondAttribute="centerY" constant="-15" id="iu4-c5-p5k"/> <constraint firstItem="iSO-mc-0TB" firstAttribute="top" secondItem="X2m-IC-J1u" secondAttribute="top" constant="95" id="jPM-Uo-0lS"/> - <constraint firstItem="pfo-D0-W7b" firstAttribute="leading" secondItem="HPl-mj-r5E" secondAttribute="trailing" constant="8" symbolic="YES" id="oKN-Ui-VIn"/> - <constraint firstAttribute="trailing" secondItem="Qek-aQ-NjE" secondAttribute="trailing" constant="10" id="puY-4D-ARy"/> <constraint firstItem="c94-b9-Sim" firstAttribute="leading" secondItem="X2m-IC-J1u" secondAttribute="leading" id="rvD-u3-Dug"/> - <constraint firstItem="8Cj-cK-AKZ" firstAttribute="top" secondItem="iSO-mc-0TB" secondAttribute="bottom" constant="14" id="shO-sV-GWB"/> - <constraint firstItem="cLd-wD-cSC" firstAttribute="centerY" secondItem="8Cj-cK-AKZ" secondAttribute="centerY" id="xia-sb-RNk"/> - <constraint firstItem="FFi-7t-C8U" firstAttribute="leading" secondItem="pfo-D0-W7b" secondAttribute="trailing" constant="5" id="zez-7B-WAb"/> - <constraint firstItem="HPl-mj-r5E" firstAttribute="leading" secondItem="X2m-IC-J1u" secondAttribute="leading" constant="5" id="zpN-ax-gny"/> </constraints> </view> </subviews> @@ -223,20 +138,11 @@ <constraint firstItem="X2m-IC-J1u" firstAttribute="top" secondItem="aV2-U6-JTf" secondAttribute="top" id="aXO-v9-CBF"/> <constraint firstItem="eAi-wv-a4Y" firstAttribute="trailing" secondItem="X2m-IC-J1u" secondAttribute="trailing" constant="5" id="hVX-vu-qJn"/> <constraint firstItem="oBQ-TP-qof" firstAttribute="leading" secondItem="eAi-wv-a4Y" secondAttribute="leading" id="r7R-MU-9cw"/> - <constraint firstItem="eAi-wv-a4Y" firstAttribute="trailing" secondItem="FFi-7t-C8U" secondAttribute="trailing" constant="14" id="xLc-ai-2T1"/> </constraints> </view> <connections> - <outlet property="buttonCopy" destination="cLd-wD-cSC" id="Sib-oL-uQx"/> - <outlet property="buttonInternalCopy" destination="FFi-7t-C8U" id="2ez-LZ-iZ0"/> - <outlet property="buttonMenu" destination="Qek-aQ-NjE" id="xfp-a1-YDn"/> <outlet property="searchField" destination="iSO-mc-0TB" id="1vY-Js-dGQ"/> <outlet property="searchFieldTopConstraint" destination="jPM-Uo-0lS" id="yfd-cG-1mu"/> - <outlet property="shareInternalLinkDescription" destination="pfo-D0-W7b" id="uRy-U9-bQu"/> - <outlet property="shareInternalLinkImage" destination="HPl-mj-r5E" id="CDi-ev-3eO"/> - <outlet property="shareInternalLinkLabel" destination="YYp-o8-YJP" id="rir-aT-bt5"/> - <outlet property="shareLinkImage" destination="8Cj-cK-AKZ" id="dIZ-nv-gyf"/> - <outlet property="shareLinkLabel" destination="SQW-aQ-ydN" id="nBK-WJ-oKy"/> <outlet property="sharedWithYouByImage" destination="fKv-xM-rVY" id="EJ0-sV-By8"/> <outlet property="sharedWithYouByLabel" destination="ngi-GT-jvv" id="Qay-IG-tZh"/> <outlet property="sharedWithYouByNote" destination="KHG-xj-wfG" id="4m5-u6-7RW"/> @@ -250,6 +156,130 @@ </objects> <point key="canvasLocation" x="2689.8550724637685" y="-167.41071428571428"/> </scene> + <!--Share New User Add Comment--> + <scene sceneID="JJ2-tz-qZ7"> + <objects> + <viewController storyboardIdentifier="NCShareNewUserAddComment" id="VvU-6J-pzy" customClass="NCShareNewUserAddComment" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="EtF-Pb-SYb"> + <rect key="frame" x="0.0" y="0.0" width="414" height="736"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UNN-v3-g1S"> + <rect key="frame" x="0.0" y="0.0" width="414" height="736"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rZ9-oE-c21"> + <rect key="frame" x="0.0" y="0.0" width="414" height="736"/> + <subviews> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qVy-Qr-W7j"> + <rect key="frame" x="0.0" y="0.0" width="414" height="200"/> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstAttribute="height" constant="200" id="GX8-Mb-uqf"/> + </constraints> + </view> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YMG-hf-HEX"> + <rect key="frame" x="0.0" y="200" width="414" height="536"/> + <subviews> + <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" canCancelContentTouches="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="VT0-1l-5HI"> + <rect key="frame" x="16" y="60.333333333333286" width="382" height="423.66666666666674"/> + <color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/> + <constraints> + <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="wqE-G9-M95"/> + </constraints> + <inset key="scrollIndicatorInsets" minX="0.0" minY="0.0" maxX="10" maxY="0.0"/> + <color key="textColor" systemColor="labelColor"/> + <fontDescription key="fontDescription" type="system" pointSize="14"/> + <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> + <integer key="value" value="10"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + <connections> + <outlet property="delegate" destination="VvU-6J-pzy" id="cAt-UZ-6KT"/> + </connections> + </textView> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sharing" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vkm-Pe-6qd"> + <rect key="frame" x="16.000000000000004" y="24" width="61.333333333333343" height="20.333333333333329"/> + <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="vkm-Pe-6qd" firstAttribute="leading" secondItem="YMG-hf-HEX" secondAttribute="leading" constant="16" id="Auk-mC-Mie"/> + <constraint firstAttribute="bottom" secondItem="VT0-1l-5HI" secondAttribute="bottom" priority="100" constant="52" id="FYk-Lv-5f6"/> + <constraint firstItem="vkm-Pe-6qd" firstAttribute="top" secondItem="YMG-hf-HEX" secondAttribute="top" constant="24" id="L0w-Cz-uK2"/> + <constraint firstAttribute="trailing" secondItem="VT0-1l-5HI" secondAttribute="trailing" constant="16" id="TgX-1J-iTO"/> + <constraint firstItem="VT0-1l-5HI" firstAttribute="leading" secondItem="YMG-hf-HEX" secondAttribute="leading" constant="16" id="gEq-qv-UTR"/> + <constraint firstItem="VT0-1l-5HI" firstAttribute="top" secondItem="vkm-Pe-6qd" secondAttribute="bottom" constant="16" id="ghe-aR-N1L"/> + <constraint firstAttribute="height" constant="536" id="oYk-ib-hVx"/> + </constraints> + </view> + </subviews> + </stackView> + </subviews> + <constraints> + <constraint firstItem="rZ9-oE-c21" firstAttribute="trailing" secondItem="cdt-uF-sLc" secondAttribute="trailing" id="JLe-cg-49Y"/> + <constraint firstItem="rZ9-oE-c21" firstAttribute="bottom" secondItem="cdt-uF-sLc" secondAttribute="bottom" id="fHi-hu-MpS"/> + <constraint firstItem="rZ9-oE-c21" firstAttribute="top" secondItem="cdt-uF-sLc" secondAttribute="top" id="od8-4k-3u3"/> + <constraint firstItem="rZ9-oE-c21" firstAttribute="width" secondItem="yeM-rG-mCp" secondAttribute="width" id="v9J-mK-SfO"/> + <constraint firstItem="rZ9-oE-c21" firstAttribute="leading" secondItem="cdt-uF-sLc" secondAttribute="leading" id="xze-Xh-I92"/> + </constraints> + <viewLayoutGuide key="contentLayoutGuide" id="cdt-uF-sLc"/> + <viewLayoutGuide key="frameLayoutGuide" id="yeM-rG-mCp"/> + </scrollView> + </subviews> + <viewLayoutGuide key="safeArea" id="8hH-o3-iQD"/> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstItem="UNN-v3-g1S" firstAttribute="top" secondItem="8hH-o3-iQD" secondAttribute="top" id="UD6-u2-ckg"/> + <constraint firstItem="8hH-o3-iQD" firstAttribute="bottom" secondItem="UNN-v3-g1S" secondAttribute="bottom" id="WzJ-jl-e33"/> + <constraint firstItem="VT0-1l-5HI" firstAttribute="height" relation="lessThanOrEqual" secondItem="EtF-Pb-SYb" secondAttribute="height" constant="-150" id="h3K-2H-qDr"/> + <constraint firstItem="UNN-v3-g1S" firstAttribute="leading" secondItem="8hH-o3-iQD" secondAttribute="leading" id="r39-yL-F9v"/> + <constraint firstItem="8hH-o3-iQD" firstAttribute="trailing" secondItem="UNN-v3-g1S" secondAttribute="trailing" id="rNm-B2-hl3"/> + </constraints> + </view> + <navigationItem key="navigationItem" id="uC3-gg-Wos"/> + <connections> + <outlet property="headerContainerView" destination="qVy-Qr-W7j" id="LaE-WS-v1X"/> + <outlet property="noteTextField" destination="VT0-1l-5HI" id="Ilz-T9-5BL"/> + <outlet property="sharingLabel" destination="vkm-Pe-6qd" id="NVy-Ug-ipx"/> + </connections> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="LJ3-hs-98b" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="4261" y="-168"/> + </scene> + <!--Share Advance Permission--> + <scene sceneID="59b-BB-FLA"> + <objects> + <tableViewController storyboardIdentifier="NCShareAdvancePermission" id="r5U-VP-Qhs" customClass="NCShareAdvancePermission" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController"> + <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" id="lDu-k5-2hT"> + <rect key="frame" x="0.0" y="0.0" width="414" height="736"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <prototypes> + <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="0qP-1F-pHW"> + <rect key="frame" x="0.0" y="44.666666030883789" width="414" height="43.666667938232422"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0qP-1F-pHW" id="z1u-eI-gTZ"> + <rect key="frame" x="0.0" y="0.0" width="414" height="43.666667938232422"/> + <autoresizingMask key="autoresizingMask"/> + </tableViewCellContentView> + </tableViewCell> + </prototypes> + <connections> + <outlet property="dataSource" destination="r5U-VP-Qhs" id="OET-a5-qea"/> + <outlet property="delegate" destination="r5U-VP-Qhs" id="cDp-4z-0Xt"/> + </connections> + </tableView> + </tableViewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="ITy-jR-JVD" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="3510" y="-169"/> + </scene> </scenes> <designables> <designable name="KHG-xj-wfG"> @@ -258,7 +288,14 @@ </designables> <resources> <image name="note.text" width="24" height="24"/> - <image name="shareCopy" width="329" height="329"/> - <image name="shareMenu" width="329" height="329"/> + <systemColor name="labelColor"> + <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> + <systemColor name="secondarySystemBackgroundColor"> + <color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> + <systemColor name="systemBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> </resources> </document> diff --git a/iOSClient/Share/NCShare.swift b/iOSClient/Share/NCShare.swift index b43ce3e48..fdad3c3f0 100644 --- a/iOSClient/Share/NCShare.swift +++ b/iOSClient/Share/NCShare.swift @@ -4,6 +4,7 @@ // // Created by Marino Faggiana on 17/07/2019. // Copyright © 2019 Marino Faggiana. All rights reserved. +// Copyright © 2022 Henrik Storch. All rights reserved. // // Author Marino Faggiana <marino.faggiana@nextcloud.com> // Author Henrik Storch <henrik.storch@nextcloud.com> @@ -28,7 +29,7 @@ import DropDown import NCCommunication import MarqueeLabel -class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingDelegate { +class NCShare: UIViewController, NCShareNetworkingDelegate, NCSharePagingContent { @IBOutlet weak var viewContainerConstraint: NSLayoutConstraint! @IBOutlet weak var sharedWithYouByView: UIView! @@ -38,27 +39,25 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD @IBOutlet weak var sharedWithYouByNote: MarqueeLabel! @IBOutlet weak var searchFieldTopConstraint: NSLayoutConstraint! @IBOutlet weak var searchField: UITextField! - @IBOutlet weak var shareLinkImage: UIImageView! - @IBOutlet weak var shareLinkLabel: UILabel! - @IBOutlet weak var shareInternalLinkImage: UIImageView! - @IBOutlet weak var shareInternalLinkLabel: UILabel! - @IBOutlet weak var shareInternalLinkDescription: UILabel! - @IBOutlet weak var buttonInternalCopy: UIButton! - @IBOutlet weak var buttonCopy: UIButton! - @IBOutlet weak var buttonMenu: UIButton! + var textField: UITextField? { searchField } + @IBOutlet weak var tableView: UITableView! - private let appDelegate = UIApplication.shared.delegate as! AppDelegate + weak var appDelegate = UIApplication.shared.delegate as? AppDelegate public var metadata: tableMetadata? public var sharingEnabled = true public var height: CGFloat = 0 - private var shareLinkMenuView: NCShareLinkMenuView? - private var shareUserMenuView: NCShareUserMenuView? - private var shareMenuViewWindow: UIView? + var canReshare: Bool { + guard let metadata = metadata else { return true } + return ((metadata.sharePermissionsCollaborationServices & NCGlobal.shared.permissionShareShare) != 0) + } + + var shares: (firstShareLink: tableShare?, share: [tableShare]?) = (nil, nil) + private var dropDown = DropDown() - private var networking: NCShareNetworking? + var networking: NCShareNetworking? // MARK: - View Life Cycle @@ -72,16 +71,6 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD searchField.placeholder = NSLocalizedString("_shareLinksearch_placeholder_", comment: "") - shareLinkImage.image = NCShareCommon.shared.createLinkAvatar(imageName: "sharebylink", colorCircle: NCBrandColor.shared.brandElement) - shareLinkLabel.text = NSLocalizedString("_share_link_", comment: "") - shareLinkLabel.textColor = NCBrandColor.shared.label - buttonCopy.setImage(UIImage(named: "shareCopy")?.image(color: .gray, size: 50), for: .normal) - - shareInternalLinkImage.image = NCShareCommon.shared.createLinkAvatar(imageName: "shareInternalLink", colorCircle: .gray) - shareInternalLinkLabel.text = NSLocalizedString("_share_internal_link_", comment: "") - shareInternalLinkDescription.text = NSLocalizedString("_share_internal_link_des_", comment: "") - buttonInternalCopy.setImage(UIImage(named: "shareCopy")?.image(color: .gray, size: 50), for: .normal) - tableView.dataSource = self tableView.delegate = self tableView.allowsSelection = false @@ -92,58 +81,13 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataNCShare), object: nil) - // Shared with you by ... - if let metadata = metadata, !metadata.ownerId.isEmpty, metadata.ownerId != self.appDelegate.userId { - - searchFieldTopConstraint.constant = 65 - sharedWithYouByView.isHidden = false - sharedWithYouByLabel.text = NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName - sharedWithYouByImage.image = NCUtility.shared.loadUserImage( - for: metadata.ownerId, - displayName: metadata.ownerDisplayName, - userBaseUrl: appDelegate) - let shareAction = UITapGestureRecognizer(target: self, action: #selector(openShareProfile)) - sharedWithYouByImage.addGestureRecognizer(shareAction) - let shareLabelAction = UITapGestureRecognizer(target: self, action: #selector(openShareProfile)) - sharedWithYouByLabel.addGestureRecognizer(shareLabelAction) - - if metadata.note.count > 0 { - searchFieldTopConstraint.constant = 95 - sharedWithYouByNoteImage.isHidden = false - sharedWithYouByNoteImage.image = NCUtility.shared.loadImage(named: "note.text", color: .gray) - sharedWithYouByNote.isHidden = false - sharedWithYouByNote.text = metadata.note - sharedWithYouByNote.textColor = NCBrandColor.shared.label - sharedWithYouByNote.trailingBuffer = sharedWithYouByNote.frame.width - } else { - sharedWithYouByNoteImage.isHidden = true - sharedWithYouByNote.isHidden = true - } - - let fileName = appDelegate.userBaseUrl + "-" + metadata.ownerId + ".png" - - if NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) == nil { - let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName - let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag - - NCCommunication.shared.downloadAvatar(user: metadata.ownerId, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag) { _, imageAvatar, _, etag, errorCode, _ in - - if errorCode == 0, let etag = etag, let imageAvatar = imageAvatar { - - NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) - self.sharedWithYouByImage.image = imageAvatar - - } else if errorCode == NCGlobal.shared.errorNotModified, let imageAvatar = NCManageDatabase.shared.setAvatarLoaded(fileName: fileName) { + guard let appDelegate = appDelegate, let metadata = metadata else { return } - self.sharedWithYouByImage.image = imageAvatar - } - } - } - } + checkSharedWithYou() reloadData() - networking = NCShareNetworking(metadata: metadata!, urlBase: appDelegate.urlBase, view: self.view, delegate: self) + networking = NCShareNetworking(metadata: metadata, urlBase: appDelegate.urlBase, view: self.view, delegate: self) if sharingEnabled { let isVisible = (self.navigationController?.topViewController as? NCSharePaging)?.indexPage == .sharing networking?.readShare(showLoadingIndicator: isVisible) @@ -151,11 +95,81 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD // changeTheming NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changePermissions(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShareChangePermissions), object: nil) changeTheming() } + func makeNewLinkShare() { + guard + let advancePermission = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "NCShareAdvancePermission") as? NCShareAdvancePermission, + let navigationController = self.navigationController, + let metadata = self.metadata else { return } + self.checkEnforcedPassword(shareType: NCShareCommon.shared.SHARE_TYPE_LINK) { password in + advancePermission.networking = self.networking + advancePermission.share = NCTableShareOptions.shareLink(metadata: metadata, password: password) + advancePermission.metadata = self.metadata + navigationController.pushViewController(advancePermission, animated: true) + } + } + + // Shared with you by ... + func checkSharedWithYou() { + guard let appDelegate = self.appDelegate, let metadata = metadata, !metadata.ownerId.isEmpty, metadata.ownerId != appDelegate.userId else { return } + + if !canReshare { + searchField.isEnabled = false + searchField.placeholder = NSLocalizedString("_share_reshare_disabled_", comment: "") + } + + searchFieldTopConstraint.constant = 65 + sharedWithYouByView.isHidden = false + sharedWithYouByLabel.text = NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName + sharedWithYouByImage.image = NCUtility.shared.loadUserImage( + for: metadata.ownerId, + displayName: metadata.ownerDisplayName, + userBaseUrl: appDelegate) + sharedWithYouByLabel.accessibilityHint = NSLocalizedString("_show_profile_", comment: "") + + let shareAction = UITapGestureRecognizer(target: self, action: #selector(openShareProfile)) + sharedWithYouByImage.addGestureRecognizer(shareAction) + let shareLabelAction = UITapGestureRecognizer(target: self, action: #selector(openShareProfile)) + sharedWithYouByLabel.addGestureRecognizer(shareLabelAction) + + if !metadata.note.isEmpty { + searchFieldTopConstraint.constant = 95 + sharedWithYouByNoteImage.isHidden = false + sharedWithYouByNoteImage.image = NCUtility.shared.loadImage(named: "note.text", color: .gray) + sharedWithYouByNote.isHidden = false + sharedWithYouByNote.text = metadata.note + sharedWithYouByNote.textColor = NCBrandColor.shared.label + sharedWithYouByNote.trailingBuffer = sharedWithYouByNote.frame.width + } else { + sharedWithYouByNoteImage.isHidden = true + sharedWithYouByNote.isHidden = true + } + + let fileName = appDelegate.userBaseUrl + "-" + metadata.ownerId + ".png" + + if NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) == nil { + let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName + let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag + + NCCommunication.shared.downloadAvatar( + user: metadata.ownerId, + fileNameLocalPath: fileNameLocalPath, + sizeImage: NCGlobal.shared.avatarSize, + avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, + etag: etag) { _, imageAvatar, _, etag, errorCode, _ in + if errorCode == 0, let etag = etag, let imageAvatar = imageAvatar { + NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) + self.sharedWithYouByImage.image = imageAvatar + } else if errorCode == NCGlobal.shared.errorNotModified, let imageAvatar = NCManageDatabase.shared.setAvatarLoaded(fileName: fileName) { + self.sharedWithYouByImage.image = imageAvatar + } + } + } + } + // MARK: - Notification Center @objc func openShareProfile() { @@ -167,34 +181,11 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD tableView.reloadData() } - @objc func changePermissions(_ notification: NSNotification) { - - if let userInfo = notification.userInfo as NSDictionary? { - if let idShare = userInfo["idShare"] as? Int, let permissions = userInfo["permissions"] as? Int, let hideDownload = userInfo["hideDownload"] as? Bool { - networking?.updateShare(idShare: idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: hideDownload) - } - } - } - // MARK: - @objc func reloadData() { - let shares = NCManageDatabase.shared.getTableShares(metadata: metadata!) - if shares.firstShareLink == nil { - buttonMenu.setImage(UIImage(named: "shareAdd")?.image(color: .gray, size: 50), for: .normal) - buttonCopy.isHidden = true - } else { - buttonMenu.setImage(UIImage(named: "shareMenu")?.image(color: .gray, size: 50), for: .normal) - buttonCopy.isHidden = false - - shareLinkLabel.text = NSLocalizedString("_share_link_", comment: "") - if shares.firstShareLink?.label.count ?? 0 > 0 { - if let shareLinkLabel = shareLinkLabel { - if let label = shares.firstShareLink?.label { - shareLinkLabel.text = NSLocalizedString("_share_link_", comment: "") + " (" + label + ")" - } - } - } + if let metadata = metadata { + shares = NCManageDatabase.shared.getTableShares(metadata: metadata) } tableView.reloadData() } @@ -202,78 +193,17 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD // MARK: - IBAction @IBAction func searchFieldDidEndOnExit(textField: UITextField) { - - guard let searchString = textField.text else { return } - + guard let searchString = textField.text, !searchString.isEmpty else { return } networking?.getSharees(searchString: searchString) } - @IBAction func touchUpInsideButtonCopy(_ sender: Any) { - - guard let metadata = self.metadata else { return } - - let shares = NCManageDatabase.shared.getTableShares(metadata: metadata) - tapCopy(with: shares.firstShareLink, sender: sender) - } - - @IBAction func touchUpInsideButtonCopyInernalLink(_ sender: Any) { - - guard let metadata = self.metadata else { return } - - let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName - NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account, queue: .main) { _, metadata, errorCode, errorDescription in - if errorCode == 0 && metadata != nil { - let internalLink = self.appDelegate.urlBase + "/index.php/f/" + metadata!.fileId - NCShareCommon.shared.copyLink(link: internalLink, viewController: self, sender: sender) - } else { - NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } - } - } - - func checkEnforcedPassword(callback: @escaping (String?) -> Void) { + func checkEnforcedPassword(shareType: Int, completion: @escaping (String?) -> Void) { guard let metadata = self.metadata, - NCManageDatabase.shared.getCapabilitiesServerBool(account: metadata.account, elements: NCElementsJSON.shared.capabilitiesFileSharingPubPasswdEnforced, exists: false) - else { return callback(nil) } - - let alertController = UIAlertController(title: NSLocalizedString("_enforce_password_protection_", comment: ""), message: "", preferredStyle: .alert) - alertController.addTextField { textField in - textField.isSecureTextEntry = true - } - alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .default) { _ in }) - let okAction = UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default) { _ in - let password = alertController.textFields?.first?.text - callback(password) - } + NCManageDatabase.shared.getCapabilitiesServerBool(account: metadata.account, elements: NCElementsJSON.shared.capabilitiesFileSharingPubPasswdEnforced, exists: false), + shareType == NCShareCommon.shared.SHARE_TYPE_LINK || shareType == NCShareCommon.shared.SHARE_TYPE_EMAIL + else { return completion(nil) } - alertController.addAction(okAction) - - self.present(alertController, animated: true, completion:nil) - } - - @IBAction func touchUpInsideButtonMenu(_ sender: Any) { - - guard let metadata = self.metadata else { return } - let shares = NCManageDatabase.shared.getTableShares(metadata: metadata) - - if shares.firstShareLink == nil { - checkEnforcedPassword { password in - self.networking?.createShareLink(password: password) - } - } else { - tapMenu(with: shares.firstShareLink!, sender: sender) - } - } - - @objc func tapLinkMenuViewWindow(gesture: UITapGestureRecognizer) { - shareLinkMenuView?.unLoad() - shareLinkMenuView = nil - shareUserMenuView?.unLoad() - shareUserMenuView = nil - } - - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - return gestureRecognizer.view == touch.view + self.present(UIAlertController.password(titleKey: "_enforce_password_protection_", completion: completion), animated: true) } // MARK: - NCShareNetworkingDelegate @@ -286,7 +216,9 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataNCShare) } - func unShareCompleted() { } + func unShareCompleted() { + self.reloadData() + } func updateShareWithError(idShare: Int) { self.reloadData() @@ -294,7 +226,7 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD func getSharees(sharees: [NCCommunicationSharee]?) { - guard let sharees = sharees else { return } + guard let sharees = sharees, let appDelegate = appDelegate else { return } dropDown = DropDown() let appearance = DropDown.appearance() @@ -311,7 +243,7 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD for sharee in sharees { var label = sharee.label if sharee.shareType == NCShareCommon.shared.SHARE_TYPE_CIRCLE { - label += " (" + sharee.circleInfo + ", " + sharee.circleOwner + ")" + label += " (\(sharee.circleInfo), \(sharee.circleOwner))" } dropDown.dataSource.append(label) } @@ -321,50 +253,25 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD dropDown.width = searchField.bounds.width dropDown.direction = .bottom - dropDown.cellNib = UINib(nibName: "NCShareUserDropDownCell", bundle: nil) - dropDown.customCellConfiguration = { (index: Index, _: String, cell: DropDownCell) -> Void in - guard let cell = cell as? NCShareUserDropDownCell else { return } + dropDown.cellNib = UINib(nibName: "NCSearchUserDropDownCell", bundle: nil) + dropDown.customCellConfiguration = { (index: Index, _, cell: DropDownCell) -> Void in + guard let cell = cell as? NCSearchUserDropDownCell else { return } let sharee = sharees[index] - cell.imageItem.image = NCShareCommon.shared.getImageShareType(shareType: sharee.shareType) - cell.imageShareeType.image = NCShareCommon.shared.getImageShareType(shareType: sharee.shareType) - let status = NCUtility.shared.getUserStatus(userIcon: sharee.userIcon, userStatus: sharee.userStatus, userMessage: sharee.userMessage) - cell.imageStatus.image = status.onlineStatus - cell.status.text = status.statusMessage - if cell.status.text?.count ?? 0 > 0 { - cell.centerTitle.constant = -5 - } else { - cell.centerTitle.constant = 0 - } - - cell.imageItem.image = NCUtility.shared.loadUserImage( - for: sharee.shareWith, - displayName: nil, - userBaseUrl: self.appDelegate) - - let fileName = self.appDelegate.userBaseUrl + "-" + sharee.shareWith + ".png" - if NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) == nil { - let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName - let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag - - NCCommunication.shared.downloadAvatar(user: sharee.shareWith, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag) { _, imageAvatar, _, etag, errorCode, _ in - - if errorCode == 0, let etag = etag, let imageAvatar = imageAvatar { - - NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) - cell.imageItem.image = imageAvatar - - } else if errorCode == NCGlobal.shared.errorNotModified, let imageAvatar = NCManageDatabase.shared.setAvatarLoaded(fileName: fileName) { - - cell.imageItem.image = imageAvatar - } - } - } + cell.setupCell(sharee: sharee, baseUrl: appDelegate) } - dropDown.selectionAction = { (index, item) in + dropDown.selectionAction = { index, _ in let sharee = sharees[index] - self.checkEnforcedPassword { password in - self.networking?.createShare(shareWith: sharee.shareWith, shareType: sharee.shareType, password: password, metadata: self.metadata!) + guard + let advancePermission = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "NCShareAdvancePermission") as? NCShareAdvancePermission, + let navigationController = self.navigationController, + let metadata = self.metadata else { return } + self.checkEnforcedPassword(shareType: sharee.shareType) { password in + let shareOptions = NCTableShareOptions(sharee: sharee, metadata: metadata, password: password) + advancePermission.share = shareOptions + advancePermission.networking = self.networking + advancePermission.metadata = metadata + navigationController.pushViewController(advancePermission, animated: true) } } @@ -377,6 +284,10 @@ class NCShare: UIViewController, UIGestureRecognizerDelegate, NCShareNetworkingD extension NCShare: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == 0, indexPath.row == 0 { + // internal cell has description + return 90 + } return 70 } } @@ -386,82 +297,48 @@ extension NCShare: UITableViewDelegate { extension NCShare: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { - return 1 + return 2 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - - var numOfRows = 0 - let shares = NCManageDatabase.shared.getTableShares(metadata: metadata!) - - if shares.share != nil { - numOfRows = shares.share!.count - } - - return numOfRows + // don't allow link creation if reshare is disabled + guard section != 0 else { return shares.firstShareLink != nil || canReshare ? 2 : 1 } + return shares.share?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + // Setup default share cells + guard indexPath.section != 0 else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellLink", for: indexPath) as? NCShareLinkCell + else { return UITableViewCell() } + cell.delegate = self + if indexPath.row == 0 { + cell.isInternalLink = true + } else if shares.firstShareLink?.isInvalidated != true { + cell.tableShare = shares.firstShareLink + } + cell.setupCellUI() + return cell + } - let shares = NCManageDatabase.shared.getTableShares(metadata: metadata!) - let tableShare = shares.share![indexPath.row] + guard let appDelegate = appDelegate, let tableShare = shares.share?[indexPath.row] else { return UITableViewCell() } // LINK - if tableShare.shareType == 3 { + if tableShare.shareType == NCShareCommon.shared.SHARE_TYPE_LINK { if let cell = tableView.dequeueReusableCell(withIdentifier: "cellLink", for: indexPath) as? NCShareLinkCell { cell.tableShare = tableShare cell.delegate = self - cell.labelTitle.text = NSLocalizedString("_share_link_", comment: "") - if tableShare.label.count > 0 { - cell.labelTitle.text = NSLocalizedString("_share_link_", comment: "") + " (" + tableShare.label + ")" - } - cell.labelTitle.textColor = NCBrandColor.shared.label + cell.setupCellUI() return cell } } else { - // USER + // USER / GROUP etc. if let cell = tableView.dequeueReusableCell(withIdentifier: "cellUser", for: indexPath) as? NCShareUserCell { - cell.tableShare = tableShare cell.delegate = self - cell.labelTitle.text = tableShare.shareWithDisplayname - cell.labelTitle.textColor = NCBrandColor.shared.label - cell.isUserInteractionEnabled = true - cell.labelQuickStatus.isHidden = false - cell.imageDownArrow.isHidden = false - cell.buttonMenu.isHidden = false - cell.imageItem.image = NCShareCommon.shared.getImageShareType(shareType: tableShare.shareType) - - let status = NCUtility.shared.getUserStatus(userIcon: tableShare.userIcon, userStatus: tableShare.userStatus, userMessage: tableShare.userMessage) - cell.imageStatus.image = status.onlineStatus - cell.status.text = status.statusMessage - + 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) - - // If the initiator or the recipient is not the current user, show the list of sharees without any options to edit it. - if tableShare.uidOwner != self.appDelegate.userId && tableShare.uidFileOwner != self.appDelegate.userId { - cell.isUserInteractionEnabled = false - cell.labelQuickStatus.isHidden = true - cell.imageDownArrow.isHidden = true - cell.buttonMenu.isHidden = true - } - - cell.btnQuickStatus.setTitle("", for: .normal) - cell.btnQuickStatus.contentHorizontalAlignment = .left - - if tableShare.permissions == NCGlobal.shared.permissionCreateShare { - cell.labelQuickStatus.text = NSLocalizedString("_share_file_drop_", comment: "") - } else { - // Read Only - if CCUtility.isAnyPermission(toEdit: tableShare.permissions) { - cell.labelQuickStatus.text = NSLocalizedString("_share_editing_", comment: "") - } else { - cell.labelQuickStatus.text = NSLocalizedString("_share_read_only_", comment: "") - } - } - return cell } } @@ -469,53 +346,3 @@ extension NCShare: UITableViewDataSource { return UITableViewCell() } } - -// MARK: - NCCell Delegates -extension NCShare: NCShareLinkCellDelegate, NCShareUserCellDelegate { - - func tapCopy(with tableShare: tableShare?, sender: Any) { - - if let link = tableShare?.url { - NCShareCommon.shared.copyLink(link: link, viewController: self, sender: sender) - } - } - - func tapMenu(with tableShare: tableShare?, sender: Any) { - - guard let tableShare = tableShare else { return } - - if tableShare.shareType == 3 { - let views = NCShareCommon.shared.openViewMenuShareLink(shareViewController: self, tableShare: tableShare, metadata: metadata!) - shareLinkMenuView = views.shareLinkMenuView - shareMenuViewWindow = views.viewWindow - - let tap = UITapGestureRecognizer(target: self, action: #selector(tapLinkMenuViewWindow)) - tap.delegate = self - shareMenuViewWindow?.addGestureRecognizer(tap) - } else { - let views = NCShareCommon.shared.openViewMenuUser(shareViewController: self, tableShare: tableShare, metadata: metadata!) - shareUserMenuView = views.shareUserMenuView - shareMenuViewWindow = views.viewWindow - - let tap = UITapGestureRecognizer(target: self, action: #selector(tapLinkMenuViewWindow)) - tap.delegate = self - shareMenuViewWindow?.addGestureRecognizer(tap) - } - } - - func showProfile(with tableShare: tableShare?, sender: Any) { - guard let tableShare = tableShare else { return } - showProfileMenu(userId: tableShare.shareWith) - } - - func quickStatus(with tableShare: tableShare?, sender: Any) { - - guard let tableShare = tableShare else { return } - - if tableShare.shareType != NCGlobal.shared.permissionDefaultFileRemoteShareNoSupportShareOption { - - let quickStatusMenu = NCShareQuickStatusMenu() - quickStatusMenu.toggleMenu(viewController: self, directory: metadata!.directory, tableShare: tableShare) - } - } -} diff --git a/iOSClient/Share/NCShareCommentsCell.swift b/iOSClient/Share/NCShareCommentsCell.swift index 36a31aae7..77335907b 100644 --- a/iOSClient/Share/NCShareCommentsCell.swift +++ b/iOSClient/Share/NCShareCommentsCell.swift @@ -38,24 +38,16 @@ class NCShareCommentsCell: UITableViewCell, NCCellProtocol { weak var delegate: NCShareCommentsCellDelegate? var filePreviewImageView: UIImageView? { - get { - return nil - } + return nil } var fileAvatarImageView: UIImageView? { - get { - return imageItem - } + return imageItem } var fileObjectId: String? { - get { - return nil - } + return nil } var fileUser: String? { - get { - return tableComments?.actorId - } + return tableComments?.actorId } override func awakeFromNib() { diff --git a/iOSClient/Share/NCShareCommon.swift b/iOSClient/Share/NCShareCommon.swift index 54ad9d29d..a33b7b2e8 100644 --- a/iOSClient/Share/NCShareCommon.swift +++ b/iOSClient/Share/NCShareCommon.swift @@ -21,7 +21,6 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. import UIKit -import FSCalendar import DropDown class NCShareCommon: NSObject { @@ -45,7 +44,7 @@ class NCShareCommon: NSObject { let size: CGFloat = 200 - let bottomImage = UIImage(named: "circle.fill")!.image(color: colorCircle, size: size/2) + let bottomImage = UIImage(named: "circle_fill")!.image(color: colorCircle, size: size/2) let topImage = UIImage(named: imageName)!.image(color: .white, size: size/2) UIGraphicsBeginImageContextWithOptions(CGSize(width: size, height: size), false, UIScreen.main.scale) bottomImage.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: size, height: size))) @@ -56,126 +55,6 @@ class NCShareCommon: NSObject { return image } - func openViewMenuShareLink(shareViewController: NCShare, tableShare: tableShare?, metadata: tableMetadata) -> (shareLinkMenuView: NCShareLinkMenuView, viewWindow: UIView) { - - var shareLinkMenuView: NCShareLinkMenuView - let window = UIApplication.shared.keyWindow! - let viewWindow = UIView(frame: window.bounds) - - window.addSubview(viewWindow) - viewWindow.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - if metadata.directory { - shareLinkMenuView = Bundle.main.loadNibNamed("NCShareLinkFolderMenuView", owner: self, options: nil)?.first as! NCShareLinkMenuView - } else { - shareLinkMenuView = Bundle.main.loadNibNamed("NCShareLinkMenuView", owner: self, options: nil)?.first as! NCShareLinkMenuView - } - - shareLinkMenuView.width = 250 - if metadata.directory { - shareLinkMenuView.height = 600 - } else { - shareLinkMenuView.height = 500 - } - - shareLinkMenuView.backgroundColor = NCBrandColor.shared.systemBackground - shareLinkMenuView.metadata = metadata - shareLinkMenuView.viewWindow = viewWindow - shareLinkMenuView.shareViewController = shareViewController - shareLinkMenuView.reloadData(idShare: tableShare?.idShare ?? 0) - shareLinkMenuView.translatesAutoresizingMaskIntoConstraints = false - viewWindow.addSubview(shareLinkMenuView) - - NSLayoutConstraint.activate([ - shareLinkMenuView.widthAnchor.constraint(equalToConstant: shareLinkMenuView.width), - shareLinkMenuView.heightAnchor.constraint(equalToConstant: shareLinkMenuView.height), - shareLinkMenuView.centerXAnchor.constraint(equalTo: viewWindow.centerXAnchor), - shareLinkMenuView.centerYAnchor.constraint(equalTo: viewWindow.centerYAnchor) - ]) - - return(shareLinkMenuView: shareLinkMenuView, viewWindow: viewWindow) - } - - func openViewMenuUser(shareViewController: NCShare, tableShare: tableShare?, metadata: tableMetadata) -> (shareUserMenuView: NCShareUserMenuView, viewWindow: UIView) { - - var shareUserMenuView: NCShareUserMenuView - let window = UIApplication.shared.keyWindow! - let viewWindow = UIView(frame: window.bounds) - - window.addSubview(viewWindow) - viewWindow.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - if metadata.directory { - shareUserMenuView = Bundle.main.loadNibNamed("NCShareUserFolderMenuView", owner: self, options: nil)?.first as! NCShareUserMenuView - } else { - shareUserMenuView = Bundle.main.loadNibNamed("NCShareUserMenuView", owner: self, options: nil)?.first as! NCShareUserMenuView - } - - shareUserMenuView.width = 250 - if metadata.directory { - shareUserMenuView.height = 420 - } else { - shareUserMenuView.height = 270 - } - - shareUserMenuView.backgroundColor = NCBrandColor.shared.systemBackground - shareUserMenuView.metadata = metadata - shareUserMenuView.viewWindow = viewWindow - shareUserMenuView.shareViewController = shareViewController - shareUserMenuView.reloadData(idShare: tableShare?.idShare ?? 0) - shareUserMenuView.translatesAutoresizingMaskIntoConstraints = false - viewWindow.addSubview(shareUserMenuView) - - NSLayoutConstraint.activate([ - shareUserMenuView.widthAnchor.constraint(equalToConstant: shareUserMenuView.width), - shareUserMenuView.heightAnchor.constraint(equalToConstant: shareUserMenuView.height), - shareUserMenuView.centerXAnchor.constraint(equalTo: viewWindow.centerXAnchor), - shareUserMenuView.centerYAnchor.constraint(equalTo: viewWindow.centerYAnchor) - ]) - - return(shareUserMenuView: shareUserMenuView, viewWindow: viewWindow) - } - - func openCalendar(view: UIView, width: CGFloat, height: CGFloat) -> (calendarView: FSCalendar, viewWindow: UIView) { - - let globalPoint = view.superview?.convert(view.frame.origin, to: nil) - - let window = UIApplication.shared.keyWindow! - let viewWindow = UIView(frame: window.bounds) - window.addSubview(viewWindow) - - let calendar = FSCalendar(frame: CGRect(x: globalPoint!.x + 10, y: globalPoint!.y + 10, width: width - 20, height: 300)) - - if #available(iOS 13.0, *) { - calendar.appearance.headerTitleColor = .label - } else { - calendar.appearance.headerTitleColor = .black - } - calendar.backgroundColor = NCBrandColor.shared.systemBackground - calendar.placeholderType = .none - calendar.appearance.headerMinimumDissolvedAlpha = 0.0 - - calendar.layer.borderColor = UIColor.lightGray.cgColor - calendar.layer.borderWidth = 0.5 - calendar.layer.masksToBounds = false - calendar.layer.cornerRadius = 5 - calendar.layer.masksToBounds = false - calendar.layer.shadowOffset = CGSize(width: 2, height: 2) - calendar.layer.shadowOpacity = 0.2 - - calendar.appearance.headerTitleFont = UIFont.systemFont(ofSize: 13) - - calendar.appearance.weekdayTextColor = NCBrandColor.shared.gray - calendar.appearance.weekdayFont = UIFont.systemFont(ofSize: 13) - - calendar.appearance.todayColor = NCBrandColor.shared.brandElement - calendar.appearance.titleFont = UIFont.systemFont(ofSize: 13) - - viewWindow.addSubview(calendar) - - return(calendarView: calendar, viewWindow: viewWindow) - } - func copyLink(link: String, viewController: UIViewController, sender: Any) { let objectsToShare = [link] @@ -187,8 +66,9 @@ class NCShareCommon: NSObject { activityViewController.popoverPresentationController?.sourceRect = (sender as AnyObject).bounds } } - - viewController.present(activityViewController, animated: true, completion: nil) + DispatchQueue.main.async { + viewController.present(activityViewController, animated: true, completion: nil) + } } func getImageShareType(shareType: Int) -> UIImage? { diff --git a/iOSClient/Share/NCShareHeaderView.xib b/iOSClient/Share/NCShareHeaderView.xib index 82e614d86..841623f6a 100644 --- a/iOSClient/Share/NCShareHeaderView.xib +++ b/iOSClient/Share/NCShareHeaderView.xib @@ -1,27 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina4_0" orientation="portrait" appearance="light"/> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> + <device id="retina4_0" orientation="landscape" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> <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"/> <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareHeaderView" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="320" height="568"/> + <rect key="frame" x="0.0" y="0.0" width="474" height="100"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="79H-PA-1m2"> - <rect key="frame" x="100" y="70" width="120" height="120"/> + <rect key="frame" x="20" y="0.0" width="100" height="100"/> <constraints> - <constraint firstAttribute="height" constant="120" id="Imw-QI-Org"/> - <constraint firstAttribute="width" constant="120" id="YKb-24-fln"/> + <constraint firstAttribute="width" secondItem="79H-PA-1m2" secondAttribute="height" id="kPd-Ha-PKN"/> </constraints> </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="headTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="n1G-pn-D8s" customClass="MarqueeLabel" customModule="MarqueeLabel"> - <rect key="frame" x="15" y="520" width="290" height="18"/> + <rect key="frame" x="128" y="8" width="326" height="18"/> <fontDescription key="fontDescription" type="system" pointSize="15"/> <nil key="textColor"/> <nil key="highlightedColor"/> @@ -29,43 +28,44 @@ <userDefinedRuntimeAttribute type="boolean" keyPath="tapToScroll" value="YES"/> </userDefinedRuntimeAttributes> </label> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bOQ-tC-40T"> - <rect key="frame" x="40" y="543" width="265" height="15"/> - <fontDescription key="fontDescription" type="system" pointSize="12"/> - <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/> - <nil key="highlightedColor"/> - </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EaW-fI-EmD"> - <rect key="frame" x="15" y="542" width="15" height="15"/> + <rect key="frame" x="128" y="77" width="15" height="15"/> <constraints> - <constraint firstAttribute="height" constant="15" id="Rli-bh-8fJ"/> - <constraint firstAttribute="width" constant="15" id="cU5-4t-7Qf"/> + <constraint firstAttribute="width" constant="15" id="GQs-ak-G4R"/> + <constraint firstAttribute="height" constant="15" id="gFH-uD-FZ0"/> </constraints> - <state key="normal" image="favorite"/> + <state key="normal" image="star.fill"/> <connections> <action selector="touchUpInsideFavorite:" destination="iN0-l3-epB" eventType="touchUpInside" id="r4F-nS-R08"/> </connections> </button> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bOQ-tC-40T"> + <rect key="frame" x="148" y="77" width="31" height="15"/> + <fontDescription key="fontDescription" type="system" pointSize="12"/> + <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/> + <nil key="highlightedColor"/> + </label> </subviews> <constraints> - <constraint firstItem="EaW-fI-EmD" firstAttribute="top" secondItem="n1G-pn-D8s" secondAttribute="bottom" constant="4" id="8Jc-BA-mYt"/> - <constraint firstItem="bOQ-tC-40T" firstAttribute="top" secondItem="n1G-pn-D8s" secondAttribute="bottom" constant="5" id="IRS-qU-ubd"/> - <constraint firstAttribute="bottom" secondItem="bOQ-tC-40T" secondAttribute="bottom" constant="10" id="KvA-Oh-KeQ"/> - <constraint firstAttribute="trailing" secondItem="bOQ-tC-40T" secondAttribute="trailing" constant="15" id="LAA-Eb-HZj"/> - <constraint firstItem="n1G-pn-D8s" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="cpD-9L-mBy"/> - <constraint firstAttribute="trailing" secondItem="n1G-pn-D8s" secondAttribute="trailing" constant="15" id="g7A-jG-74Q"/> - <constraint firstItem="79H-PA-1m2" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="70" id="k8q-dk-ofe"/> - <constraint firstItem="EaW-fI-EmD" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="15" id="oRc-nv-VzO"/> - <constraint firstItem="79H-PA-1m2" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="t10-qd-7Cn"/> - <constraint firstItem="bOQ-tC-40T" firstAttribute="leading" secondItem="EaW-fI-EmD" secondAttribute="trailing" constant="10" id="uf6-8A-fN4"/> + <constraint firstAttribute="trailing" secondItem="n1G-pn-D8s" secondAttribute="trailing" constant="20" id="0ss-6p-laS"/> + <constraint firstItem="EaW-fI-EmD" firstAttribute="leading" secondItem="79H-PA-1m2" secondAttribute="trailing" constant="8" symbolic="YES" id="7jw-Bt-GHm"/> + <constraint firstItem="bOQ-tC-40T" firstAttribute="leading" secondItem="EaW-fI-EmD" secondAttribute="trailing" constant="5" id="HNe-K2-JNt"/> + <constraint firstAttribute="bottom" secondItem="bOQ-tC-40T" secondAttribute="bottom" constant="8" id="LkB-ps-xwS"/> + <constraint firstItem="n1G-pn-D8s" firstAttribute="leading" secondItem="79H-PA-1m2" secondAttribute="trailing" constant="8" symbolic="YES" id="ca1-QS-7XL"/> + <constraint firstItem="EaW-fI-EmD" firstAttribute="centerY" secondItem="bOQ-tC-40T" secondAttribute="centerY" id="g7i-UL-F1V"/> + <constraint firstItem="n1G-pn-D8s" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="8" id="gf7-1r-fAJ"/> + <constraint firstItem="79H-PA-1m2" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="pxE-GL-Wu8"/> + <constraint firstItem="79H-PA-1m2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="sPO-Xj-c1R"/> + <constraint firstAttribute="bottom" secondItem="79H-PA-1m2" secondAttribute="bottom" id="yMh-bh-mR7"/> </constraints> + <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <connections> <outlet property="favorite" destination="EaW-fI-EmD" id="dv5-Qo-tPx"/> <outlet property="imageView" destination="79H-PA-1m2" id="t6m-wr-OQ5"/> <outlet property="info" destination="bOQ-tC-40T" id="N7R-YH-Xek"/> <outlet property="path" destination="n1G-pn-D8s" id="ckb-qc-lqb"/> </connections> - <point key="canvasLocation" x="38" y="253"/> + <point key="canvasLocation" x="-12.67605633802817" y="45"/> </view> </objects> <designables> @@ -74,6 +74,6 @@ </designable> </designables> <resources> - <image name="favorite" width="300" height="300"/> + <image name="star.fill" width="512" height="512"/> </resources> </document> diff --git a/iOSClient/Share/NCShareLinkCell.swift b/iOSClient/Share/NCShareLinkCell.swift index 7468b78ba..fe0624e53 100644 --- a/iOSClient/Share/NCShareLinkCell.swift +++ b/iOSClient/Share/NCShareLinkCell.swift @@ -24,29 +24,65 @@ import UIKit class NCShareLinkCell: UITableViewCell { - @IBOutlet weak var imageItem: UIImageView! - @IBOutlet weak var labelTitle: UILabel! - @IBOutlet weak var buttonCopy: UIButton! - @IBOutlet weak var buttonMenu: UIButton! - - private let iconShare: CGFloat = 200 + @IBOutlet private weak var imageItem: UIImageView! + @IBOutlet private weak var labelTitle: UILabel! + @IBOutlet private weak var descriptionLabel: UILabel! + @IBOutlet private weak var menuButton: UIButton! + @IBOutlet private weak var copyButton: UIButton! var tableShare: tableShare? weak var delegate: NCShareLinkCellDelegate? + var isInternalLink = false + + override func prepareForReuse() { + super.prepareForReuse() + isInternalLink = false + tableShare = nil + } + + func setupCellUI() { + var imageName: String + var imageBGColor: UIColor + var menuImageName = "shareMenu" + + menuButton.isHidden = isInternalLink + descriptionLabel.isHidden = !isInternalLink + copyButton.isHidden = !isInternalLink && tableShare == nil + copyButton.accessibilityLabel = NSLocalizedString("_copy_", comment: "") + menuButton.accessibilityLabel = NSLocalizedString("_more_", comment: "") + + if isInternalLink { + imageName = "shareInternalLink" + imageBGColor = .gray + labelTitle.text = NSLocalizedString("_share_internal_link_", comment: "") + descriptionLabel.text = NSLocalizedString("_share_internal_link_des_", comment: "") + } else { + labelTitle.text = NSLocalizedString("_share_link_", comment: "") + if let tableShare = tableShare { + if !tableShare.label.isEmpty { + labelTitle.text? += " (\(tableShare.label))" + } + } else { + menuImageName = "shareAdd" + menuButton.accessibilityLabel = NSLocalizedString("_add_", comment: "") + } + + imageName = "sharebylink" + imageBGColor = NCBrandColor.shared.brandElement - override func awakeFromNib() { - super.awakeFromNib() + menuButton.setImage(UIImage(named: menuImageName)?.image(color: .gray, size: 50), for: .normal) + } - imageItem.image = NCShareCommon.shared.createLinkAvatar(imageName: "sharebylink", colorCircle: NCBrandColor.shared.brandElement) - buttonCopy.setImage(UIImage(named: "shareCopy")!.image(color: .gray, size: 50), for: .normal) - buttonMenu.setImage(UIImage(named: "shareMenu")!.image(color: .gray, size: 50), for: .normal) + labelTitle.textColor = NCBrandColor.shared.label + imageItem.image = NCShareCommon.shared.createLinkAvatar(imageName: imageName, colorCircle: imageBGColor) + copyButton.setImage(UIImage(named: "shareCopy")?.image(color: .gray, size: 50), for: .normal) } - @IBAction func touchUpInsideCopy(_ sender: Any) { + @IBAction func touchUpCopy(_ sender: Any) { delegate?.tapCopy(with: tableShare, sender: sender) } - @IBAction func touchUpInsideMenu(_ sender: Any) { + @IBAction func touchUpMenu(_ sender: Any) { delegate?.tapMenu(with: tableShare, sender: sender) } } diff --git a/iOSClient/Share/NCShareLinkCell.xib b/iOSClient/Share/NCShareLinkCell.xib index 81ba5aa18..b5172d16f 100755 --- a/iOSClient/Share/NCShareLinkCell.xib +++ b/iOSClient/Share/NCShareLinkCell.xib @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" 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="19529" 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="14490.49"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> + <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> @@ -15,7 +14,7 @@ <rect key="frame" x="0.0" y="0.0" width="600" height="90"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="qJF-Yc-gKE" id="3Oe-gU-3Nk"> - <rect key="frame" x="0.0" y="0.0" width="600" height="89.5"/> + <rect key="frame" x="0.0" y="0.0" width="600" height="90"/> <autoresizingMask key="autoresizingMask"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" image="circle" translatesAutoresizingMaskIntoConstraints="NO" id="qDs-UG-Mn7" userLabel="ImageItem"> @@ -25,63 +24,84 @@ <constraint firstAttribute="width" constant="40" id="GNY-Va-SIJ"/> </constraints> </imageView> - <label opaque="NO" userInteractionEnabled="NO" tag="101" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Share link" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="otH-mT-7Z4" userLabel="labelTitle"> - <rect key="frame" x="55" y="36" width="150" height="18"/> - <constraints> - <constraint firstAttribute="width" constant="150" id="4Oa-yZ-HZK"/> - <constraint firstAttribute="height" constant="18" id="iet-xr-SX6"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="15"/> - <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xaz-vY-Jzu" userLabel="ButtonCopy"> - <rect key="frame" x="520" y="35" width="20" height="20"/> - <constraints> - <constraint firstAttribute="width" constant="20" id="0JR-eM-oir"/> - <constraint firstAttribute="height" constant="20" id="HVo-ht-9m6"/> - </constraints> - <state key="normal" image="shareCopy"/> - <connections> - <action selector="touchUpInsideCopy:" destination="qJF-Yc-gKE" eventType="touchUpInside" id="hSV-RK-FAe"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="J1z-RG-U4A" userLabel="ButtonMenu"> - <rect key="frame" x="570" y="35" width="20" height="20"/> - <constraints> - <constraint firstAttribute="height" constant="20" id="G48-LB-BsD"/> - <constraint firstAttribute="width" constant="20" id="vLI-cJ-Jqx"/> - </constraints> - <state key="normal" image="shareMenu"/> - <connections> - <action selector="touchUpInsideMenu:" destination="qJF-Yc-gKE" eventType="touchUpInside" id="GT2-Ef-FfR"/> - </connections> - </button> + <stackView opaque="NO" contentMode="scaleToFill" spacing="30" translatesAutoresizingMaskIntoConstraints="NO" id="OQv-Vf-bvD"> + <rect key="frame" x="520" y="35" width="70" height="20"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xaz-vY-Jzu" userLabel="ButtonCopy"> + <rect key="frame" x="0.0" y="0.0" width="20" height="20"/> + <constraints> + <constraint firstAttribute="width" constant="20" id="0JR-eM-oir"/> + <constraint firstAttribute="height" constant="20" id="HVo-ht-9m6"/> + </constraints> + <state key="normal" image="shareCopy"/> + <connections> + <action selector="touchUpCopy:" destination="qJF-Yc-gKE" eventType="touchUpInside" id="s3f-6n-cKF"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="J1z-RG-U4A" userLabel="ButtonMenu"> + <rect key="frame" x="50" y="0.0" width="20" height="20"/> + <constraints> + <constraint firstAttribute="height" constant="20" id="G48-LB-BsD"/> + <constraint firstAttribute="width" constant="20" id="vLI-cJ-Jqx"/> + </constraints> + <state key="normal" image="shareMenu"/> + <connections> + <action selector="touchUpMenu:" destination="qJF-Yc-gKE" eventType="touchUpInside" id="hFx-Ib-xay"/> + </connections> + </button> + </subviews> + </stackView> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="Wxr-1B-Czy"> + <rect key="frame" x="53" y="12" width="452" height="66"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" tag="101" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Share link" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="otH-mT-7Z4" userLabel="labelTitle"> + <rect key="frame" x="0.0" y="0.0" width="452" height="22"/> + <fontDescription key="fontDescription" type="system" pointSize="15"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalCompressionResistancePriority="245" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WJj-9P-3bn"> + <rect key="frame" x="0.0" y="26" width="452" height="40"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Eo4-Rq-ETK"/> + </constraints> + <string key="text">Only works.... +.;...</string> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <color key="textColor" systemColor="secondaryLabelColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </stackView> </subviews> <constraints> - <constraint firstItem="otH-mT-7Z4" firstAttribute="leading" secondItem="qDs-UG-Mn7" secondAttribute="trailing" constant="10" id="7o5-Rj-6lV"/> - <constraint firstItem="otH-mT-7Z4" firstAttribute="centerY" secondItem="3Oe-gU-3Nk" secondAttribute="centerY" id="JNE-HJ-E36"/> + <constraint firstItem="OQv-Vf-bvD" firstAttribute="leading" secondItem="Wxr-1B-Czy" secondAttribute="trailing" constant="15" id="8QW-n0-4lO"/> <constraint firstItem="qDs-UG-Mn7" firstAttribute="leading" secondItem="3Oe-gU-3Nk" secondAttribute="leading" constant="5" id="KOm-wo-CBa"/> - <constraint firstItem="J1z-RG-U4A" firstAttribute="centerY" secondItem="3Oe-gU-3Nk" secondAttribute="centerY" id="TvQ-yn-L5w"/> + <constraint firstAttribute="bottom" secondItem="Wxr-1B-Czy" secondAttribute="bottom" constant="12" id="MM0-9i-BpF"/> + <constraint firstAttribute="trailing" secondItem="OQv-Vf-bvD" secondAttribute="trailing" constant="10" id="W3b-ww-vbQ"/> <constraint firstItem="qDs-UG-Mn7" firstAttribute="centerY" secondItem="3Oe-gU-3Nk" secondAttribute="centerY" id="ZrD-Aw-xkx"/> - <constraint firstItem="J1z-RG-U4A" firstAttribute="leading" secondItem="xaz-vY-Jzu" secondAttribute="trailing" constant="30" id="gGI-DA-dwq"/> - <constraint firstItem="xaz-vY-Jzu" firstAttribute="centerY" secondItem="3Oe-gU-3Nk" secondAttribute="centerY" id="o6o-Zj-1aU"/> - <constraint firstAttribute="trailing" secondItem="J1z-RG-U4A" secondAttribute="trailing" constant="10" id="pQA-B9-MM5"/> + <constraint firstItem="OQv-Vf-bvD" firstAttribute="centerY" secondItem="3Oe-gU-3Nk" secondAttribute="centerY" id="eLc-gk-xAr"/> + <constraint firstItem="Wxr-1B-Czy" firstAttribute="leading" secondItem="qDs-UG-Mn7" secondAttribute="trailing" constant="8" id="nXI-b3-EJM"/> + <constraint firstItem="Wxr-1B-Czy" firstAttribute="top" secondItem="3Oe-gU-3Nk" secondAttribute="top" constant="12" id="vxe-9X-O1f"/> </constraints> </tableViewCellContentView> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <connections> - <outlet property="buttonCopy" destination="xaz-vY-Jzu" id="WVv-oI-jD8"/> - <outlet property="buttonMenu" destination="J1z-RG-U4A" id="L2V-Ev-Cx0"/> + <outlet property="copyButton" destination="xaz-vY-Jzu" id="pMt-Zu-ORX"/> + <outlet property="descriptionLabel" destination="WJj-9P-3bn" id="QC7-SX-O3M"/> <outlet property="imageItem" destination="qDs-UG-Mn7" id="jxL-r7-BVs"/> <outlet property="labelTitle" destination="otH-mT-7Z4" id="f9z-Oa-OiR"/> + <outlet property="menuButton" destination="J1z-RG-U4A" id="VCC-y1-LRK"/> </connections> <point key="canvasLocation" x="97.599999999999994" y="276.1619190404798"/> </tableViewCell> </objects> <resources> - <image name="circle" width="329" height="329"/> + <image name="circle" width="210" height="156"/> <image name="shareCopy" width="329" height="329"/> <image name="shareMenu" width="329" height="329"/> + <systemColor name="secondaryLabelColor"> + <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> </resources> </document> diff --git a/iOSClient/Share/NCShareLinkFolderMenuView.xib b/iOSClient/Share/NCShareLinkFolderMenuView.xib deleted file mode 100644 index 8fb3aed5c..000000000 --- a/iOSClient/Share/NCShareLinkFolderMenuView.xib +++ /dev/null @@ -1,318 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina4_0" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> - <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"/> - <view opaque="NO" contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareLinkMenuView" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="250" height="600"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="sjf-wF-y07" userLabel="SwitchAllowEditing"> - <rect key="frame" x="10" y="62" width="51" height="31"/> - <connections> - <action selector="switchReadOnlyWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="IZJ-Hz-NxB"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Read only" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHP-P8-rm2"> - <rect key="frame" x="70" y="70" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="lcS-7f-bEg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="UIn-eq-hkP"> - <rect key="frame" x="10" y="215" width="51" height="31"/> - <connections> - <action selector="switchHideDownloadChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="ZUj-2h-gQC"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hide download" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KXo-cP-gkc"> - <rect key="frame" x="70" y="223" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="IEP-cO-Um6"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="HDO-WC-RvJ"> - <rect key="frame" x="10" y="266" width="51" height="31"/> - <connections> - <action selector="switchPasswordProtectChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="wGL-du-81Q"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password protect" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HaY-OM-mQh"> - <rect key="frame" x="70" y="274" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="MeH-bs-tZK"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="e1F-PV-32s"> - <rect key="frame" x="70" y="299" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="tTv-8w-kxm"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/> - <connections> - <action selector="fieldPasswordProtectDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="2PO-wf-bHs"/> - </connections> - </textField> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="dB8-1M-WZr"> - <rect key="frame" x="10" y="349" width="51" height="31"/> - <connections> - <action selector="switchSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="26x-ld-Jsj"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Set expiration date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qll-9F-4DA"> - <rect key="frame" x="70" y="357" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="KyU-PL-PRI"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ymk-0u-ddH"> - <rect key="frame" x="70" y="382" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="G4f-LN-v7k"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="editingDidBegin" id="5Ou-k5-UM5"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="file_txt" translatesAutoresizingMaskIntoConstraints="NO" id="F4T-wQ-tBU"> - <rect key="frame" x="13" y="432" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="7uC-w2-XPl"/> - <constraint firstAttribute="width" constant="25" id="YkI-0i-Hbj"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Note to recipient" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grT-sd-j7q"> - <rect key="frame" x="70" y="437" width="175" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gof-GU-toa"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FyH-3p-EdC"> - <rect key="frame" x="70" y="462" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="2CZ-EP-NrP"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldNoteToRecipientDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="Z9v-qs-dp4"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="trash" translatesAutoresizingMaskIntoConstraints="NO" id="hr8-Qe-xD0" userLabel="Image Delete Share Link"> - <rect key="frame" x="13" y="512" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="Ktg-2f-87b"/> - <constraint firstAttribute="width" constant="25" id="ZJu-Y5-U67"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delete share link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ff4-JE-zGU"> - <rect key="frame" x="70" y="517" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gYi-S0-IOg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CLA-UL-mYb"> - <rect key="frame" x="13" y="512" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="fWP-XF-kQx"/> - </constraints> - <connections> - <action selector="buttonDeleteShareLinkWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="ANe-oV-NCX"/> - </connections> - </button> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="add" translatesAutoresizingMaskIntoConstraints="NO" id="6SU-Ak-Q7m"> - <rect key="frame" x="13" y="557" width="25" height="25"/> - <constraints> - <constraint firstAttribute="width" constant="25" id="NLr-qc-w3G"/> - <constraint firstAttribute="height" constant="25" id="wkp-ks-jJM"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add another link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cna-iK-PD7"> - <rect key="frame" x="70" y="562" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="p9o-oI-gWq"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bK-Kl-hcs"> - <rect key="frame" x="13" y="557" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="wQO-6d-vWV"/> - </constraints> - <connections> - <action selector="buttonAddAnotherLinkWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="1Ea-g9-pQ8"/> - </connections> - </button> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="0ON-8M-J6K" userLabel="SwitchAllowEditing"> - <rect key="frame" x="10" y="113" width="51" height="31"/> - <connections> - <action selector="switchAllowUploadAndEditingWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="HEh-km-e1I"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Allow upload and editing" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9rD-k5-hnW"> - <rect key="frame" x="70" y="121" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="zKi-d5-7DQ"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="jpY-MU-ecU" userLabel="SwitchAllowEditing"> - <rect key="frame" x="10" y="164" width="51" height="31"/> - <connections> - <action selector="switchFileDropWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="32M-Ks-mr6"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="File drop (upload only)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QJc-Cx-Fa3"> - <rect key="frame" x="70" y="172" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="GRe-k0-V9C"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Placeholder" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="JOx-l5-TUp"> - <rect key="frame" x="10" y="12" width="230" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="eTg-X2-ih2"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fielLabelDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="s4f-hL-eZ2"/> - </connections> - </textField> - </subviews> - <constraints> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="0WP-PE-HTp"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="2RV-rL-sYG"/> - <constraint firstItem="HaY-OM-mQh" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="4CZ-Mo-lOP"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="centerY" secondItem="F4T-wQ-tBU" secondAttribute="centerY" id="4KH-Py-OgY"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="centerY" secondItem="dB8-1M-WZr" secondAttribute="centerY" id="5QL-7q-jdE"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="top" secondItem="grT-sd-j7q" secondAttribute="bottom" constant="10" id="7al-MO-ezA"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="8lb-ki-xfh"/> - <constraint firstItem="0ON-8M-J6K" firstAttribute="top" secondItem="sjf-wF-y07" secondAttribute="bottom" constant="20" id="AGy-qr-ahT"/> - <constraint firstItem="0bK-Kl-hcs" firstAttribute="top" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="20" id="Bka-Hj-Arm"/> - <constraint firstAttribute="trailing" secondItem="ymk-0u-ddH" secondAttribute="trailing" constant="10" id="Chd-iQ-EdR"/> - <constraint firstItem="KXo-cP-gkc" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="CuB-lc-pHi"/> - <constraint firstItem="UIn-eq-hkP" firstAttribute="top" secondItem="jpY-MU-ecU" secondAttribute="bottom" constant="20" id="EO5-Qr-Xxq"/> - <constraint firstItem="UIn-eq-hkP" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="FwN-Rj-koX"/> - <constraint firstItem="HDO-WC-RvJ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="GTl-Tq-cLY"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="centerY" secondItem="sjf-wF-y07" secondAttribute="centerY" id="HiA-pE-L6l"/> - <constraint firstItem="6SU-Ak-Q7m" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="Hqf-wy-u3S"/> - <constraint firstAttribute="trailing" secondItem="e1F-PV-32s" secondAttribute="trailing" constant="10" id="ImT-sr-UAr"/> - <constraint firstAttribute="trailing" secondItem="QJc-Cx-Fa3" secondAttribute="trailing" constant="10" id="JJ4-N8-Sbc"/> - <constraint firstItem="QJc-Cx-Fa3" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="Jnb-0A-4RI"/> - <constraint firstAttribute="trailing" secondItem="0bK-Kl-hcs" secondAttribute="trailing" constant="20" id="Jy3-Qb-ucs"/> - <constraint firstAttribute="trailing" secondItem="cna-iK-PD7" secondAttribute="trailing" constant="10" id="LEz-3F-g5Q"/> - <constraint firstAttribute="trailing" secondItem="grT-sd-j7q" secondAttribute="trailing" constant="5" id="Nyn-RD-jTz"/> - <constraint firstItem="6SU-Ak-Q7m" firstAttribute="top" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="20" id="OU0-vG-YTq"/> - <constraint firstAttribute="trailing" secondItem="FyH-3p-EdC" secondAttribute="trailing" constant="10" id="RhU-wl-afT"/> - <constraint firstItem="jpY-MU-ecU" firstAttribute="top" secondItem="0ON-8M-J6K" secondAttribute="bottom" constant="20" id="STB-7n-YyT"/> - <constraint firstItem="KXo-cP-gkc" firstAttribute="centerY" secondItem="UIn-eq-hkP" secondAttribute="centerY" id="Snt-zf-mcs"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="TFC-63-muN"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="top" secondItem="e1F-PV-32s" secondAttribute="bottom" constant="20" id="TIV-5L-Cza"/> - <constraint firstAttribute="trailing" secondItem="9rD-k5-hnW" secondAttribute="trailing" constant="10" id="TUb-mC-BhW"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="TXe-jF-DdS"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="top" secondItem="JOx-l5-TUp" secondAttribute="bottom" constant="20" id="UTk-iX-STG"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="Ua2-93-05m"/> - <constraint firstItem="HaY-OM-mQh" firstAttribute="centerY" secondItem="HDO-WC-RvJ" secondAttribute="centerY" id="UcN-gf-Prv"/> - <constraint firstItem="e1F-PV-32s" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="YCZ-Z6-1Ty"/> - <constraint firstItem="9rD-k5-hnW" firstAttribute="centerY" secondItem="0ON-8M-J6K" secondAttribute="centerY" id="ZHV-C3-5rz"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="Zoj-Ro-jFv"/> - <constraint firstAttribute="trailing" secondItem="IHP-P8-rm2" secondAttribute="trailing" constant="10" id="Zsj-Ja-2wq"/> - <constraint firstItem="cna-iK-PD7" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="aZc-3Q-liP"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="top" secondItem="ymk-0u-ddH" secondAttribute="bottom" constant="20" id="aj8-2w-ySe"/> - <constraint firstItem="0bK-Kl-hcs" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="bSd-GG-kS3"/> - <constraint firstItem="jpY-MU-ecU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="bkE-q5-Vpr"/> - <constraint firstItem="HDO-WC-RvJ" firstAttribute="top" secondItem="UIn-eq-hkP" secondAttribute="bottom" constant="20" id="dex-mb-oRg"/> - <constraint firstItem="9rD-k5-hnW" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="eaN-ru-7yU"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="elF-be-kqS"/> - <constraint firstItem="cna-iK-PD7" firstAttribute="centerY" secondItem="0bK-Kl-hcs" secondAttribute="centerY" id="fDj-O7-Xdr"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="top" secondItem="FyH-3p-EdC" secondAttribute="bottom" constant="20" id="itX-To-Hbm"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="top" secondItem="qll-9F-4DA" secondAttribute="bottom" constant="10" id="k4G-Yb-xBy"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="lRX-gv-77N"/> - <constraint firstAttribute="trailing" secondItem="Ff4-JE-zGU" secondAttribute="trailing" constant="10" id="ljN-WF-OVS"/> - <constraint firstItem="e1F-PV-32s" firstAttribute="top" secondItem="HaY-OM-mQh" secondAttribute="bottom" constant="10" id="m5R-ms-HZh"/> - <constraint firstItem="0ON-8M-J6K" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="mQf-ph-p16"/> - <constraint firstAttribute="trailing" secondItem="JOx-l5-TUp" secondAttribute="trailing" constant="10" id="mq5-TO-THS"/> - <constraint firstItem="JOx-l5-TUp" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="12" id="n0G-KB-JE2"/> - <constraint firstAttribute="trailing" secondItem="CLA-UL-mYb" secondAttribute="trailing" constant="20" id="oEb-Su-Nu5"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qEq-8J-iTD"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qrv-wQ-p6E"/> - <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="0bK-Kl-hcs" secondAttribute="bottom" constant="10" id="rgb-gX-oJd"/> - <constraint firstAttribute="trailing" secondItem="KXo-cP-gkc" secondAttribute="trailing" constant="10" id="uDa-6r-K84"/> - <constraint firstAttribute="trailing" secondItem="qll-9F-4DA" secondAttribute="trailing" constant="10" id="vaT-9Q-m84"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="x4S-GE-lJ8"/> - <constraint firstAttribute="trailing" secondItem="HaY-OM-mQh" secondAttribute="trailing" constant="10" id="yvI-xV-T7d"/> - <constraint firstItem="QJc-Cx-Fa3" firstAttribute="centerY" secondItem="jpY-MU-ecU" secondAttribute="centerY" id="z3L-Nd-uWl"/> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="zc7-db-OeN"/> - <constraint firstItem="JOx-l5-TUp" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="zsp-AH-Dzr"/> - </constraints> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <connections> - <outlet property="buttonAddAnotherLink" destination="0bK-Kl-hcs" id="XMo-Cp-y6f"/> - <outlet property="buttonDeleteShareLink" destination="CLA-UL-mYb" id="LVP-Vh-RHb"/> - <outlet property="fieldLabel" destination="JOx-l5-TUp" id="RTd-kU-zGz"/> - <outlet property="fieldNoteToRecipient" destination="FyH-3p-EdC" id="EBz-iA-1tq"/> - <outlet property="fieldPasswordProtect" destination="e1F-PV-32s" id="c1Y-0f-UYD"/> - <outlet property="fieldSetExpirationDate" destination="ymk-0u-ddH" id="W6o-xf-Zj2"/> - <outlet property="imageAddAnotherLink" destination="6SU-Ak-Q7m" id="Is7-q2-ZJu"/> - <outlet property="imageDeleteShareLink" destination="hr8-Qe-xD0" id="hGE-fg-rnv"/> - <outlet property="imageNoteToRecipient" destination="F4T-wQ-tBU" id="IHc-Q2-VxM"/> - <outlet property="labelAddAnotherLink" destination="cna-iK-PD7" id="xax-ip-GpR"/> - <outlet property="labelAllowUploadAndEditing" destination="9rD-k5-hnW" id="rqA-Me-ERg"/> - <outlet property="labelDeleteShareLink" destination="Ff4-JE-zGU" id="7jH-iS-zf8"/> - <outlet property="labelFileDrop" destination="QJc-Cx-Fa3" id="mN5-D6-JIs"/> - <outlet property="labelHideDownload" destination="KXo-cP-gkc" id="vis-Zu-pga"/> - <outlet property="labelNoteToRecipient" destination="grT-sd-j7q" id="yOv-Ke-EnF"/> - <outlet property="labelPasswordProtect" destination="HaY-OM-mQh" id="nlG-u7-v1H"/> - <outlet property="labelReadOnly" destination="IHP-P8-rm2" id="Rgw-5M-bf6"/> - <outlet property="labelSetExpirationDate" destination="qll-9F-4DA" id="K8B-Ys-V8e"/> - <outlet property="switchAllowUploadAndEditing" destination="0ON-8M-J6K" id="cY7-1g-kDC"/> - <outlet property="switchFileDrop" destination="jpY-MU-ecU" id="gs6-3e-de6"/> - <outlet property="switchHideDownload" destination="UIn-eq-hkP" id="25D-zI-MEj"/> - <outlet property="switchPasswordProtect" destination="HDO-WC-RvJ" id="K7a-3i-8kx"/> - <outlet property="switchReadOnly" destination="sjf-wF-y07" id="QSL-7L-cdn"/> - <outlet property="switchSetExpirationDate" destination="dB8-1M-WZr" id="R5d-Hf-ga6"/> - </connections> - <point key="canvasLocation" x="-142.5" y="519.71830985915494"/> - </view> - </objects> - <resources> - <image name="add" width="25" height="25"/> - <image name="file_txt" width="300" height="300"/> - <image name="trash" width="512" height="512"/> - </resources> -</document> diff --git a/iOSClient/Share/NCShareLinkMenuView.swift b/iOSClient/Share/NCShareLinkMenuView.swift deleted file mode 100644 index 19993dc42..000000000 --- a/iOSClient/Share/NCShareLinkMenuView.swift +++ /dev/null @@ -1,484 +0,0 @@ -// -// NCShareLinkMenuView.swift -// Nextcloud -// -// Created by Marino Faggiana on 25/07/2019. -// Copyright © 2019 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 FSCalendar -import NCCommunication - -class NCShareLinkMenuView: UIView, UIGestureRecognizerDelegate, UITextFieldDelegate, NCShareNetworkingDelegate, FSCalendarDelegate, FSCalendarDelegateAppearance { - - @IBOutlet weak var fieldLabel: UITextField! - - @IBOutlet weak var switchAllowEditing: UISwitch! - @IBOutlet weak var labelAllowEditing: UILabel! - - @IBOutlet weak var switchReadOnly: UISwitch! - @IBOutlet weak var labelReadOnly: UILabel! - - @IBOutlet weak var switchAllowUploadAndEditing: UISwitch! - @IBOutlet weak var labelAllowUploadAndEditing: UILabel! - - @IBOutlet weak var switchFileDrop: UISwitch! - @IBOutlet weak var labelFileDrop: UILabel! - - @IBOutlet weak var switchHideDownload: UISwitch! - @IBOutlet weak var labelHideDownload: UILabel! - - @IBOutlet weak var switchPasswordProtect: UISwitch! - @IBOutlet weak var labelPasswordProtect: UILabel! - @IBOutlet weak var fieldPasswordProtect: UITextField! - - @IBOutlet weak var switchSetExpirationDate: UISwitch! - @IBOutlet weak var labelSetExpirationDate: UILabel! - @IBOutlet weak var fieldSetExpirationDate: UITextField! - - @IBOutlet weak var imageNoteToRecipient: UIImageView! - @IBOutlet weak var labelNoteToRecipient: UILabel! - @IBOutlet weak var fieldNoteToRecipient: UITextField! - - @IBOutlet weak var buttonDeleteShareLink: UIButton! - @IBOutlet weak var labelDeleteShareLink: UILabel! - @IBOutlet weak var imageDeleteShareLink: UIImageView! - - @IBOutlet weak var buttonAddAnotherLink: UIButton! - @IBOutlet weak var labelAddAnotherLink: UILabel! - @IBOutlet weak var imageAddAnotherLink: UIImageView! - - private let appDelegate = UIApplication.shared.delegate as! AppDelegate - - var width: CGFloat = 0 - var height: CGFloat = 0 - - private var tableShare: tableShare? - var metadata: tableMetadata? - var shareViewController: NCShare? - private var networking: NCShareNetworking? - - var viewWindow: UIView? - var viewWindowCalendar: UIView? - private var calendar: FSCalendar? - private var activeTextfieldDiff: CGFloat = 0 - private var activeTextField = UITextField() - - override func awakeFromNib() { - - layer.borderColor = UIColor.lightGray.cgColor - layer.borderWidth = 0.5 - layer.cornerRadius = 5 - layer.masksToBounds = false - layer.shadowOffset = CGSize(width: 2, height: 2) - layer.shadowOpacity = 0.2 - - fieldLabel?.placeholder = NSLocalizedString("_Link_name_", comment: "") - - switchAllowEditing?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchAllowEditing?.onTintColor = NCBrandColor.shared.brandElement - switchReadOnly?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchReadOnly?.onTintColor = NCBrandColor.shared.brandElement - switchAllowUploadAndEditing?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchAllowUploadAndEditing?.onTintColor = NCBrandColor.shared.brandElement - switchFileDrop?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchFileDrop?.onTintColor = NCBrandColor.shared.brandElement - switchHideDownload.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchHideDownload.onTintColor = NCBrandColor.shared.brandElement - switchPasswordProtect.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchPasswordProtect.onTintColor = NCBrandColor.shared.brandElement - switchSetExpirationDate.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchSetExpirationDate.onTintColor = NCBrandColor.shared.brandElement - - labelAllowEditing?.text = NSLocalizedString("_share_allow_editing_", comment: "") - labelAllowEditing?.textColor = NCBrandColor.shared.label - labelReadOnly?.text = NSLocalizedString("_share_read_only_", comment: "") - labelReadOnly?.textColor = NCBrandColor.shared.label - labelAllowUploadAndEditing?.text = NSLocalizedString("_share_allow_upload_", comment: "") - labelAllowUploadAndEditing?.textColor = NCBrandColor.shared.label - labelFileDrop?.text = NSLocalizedString("_share_file_drop_", comment: "") - labelFileDrop?.textColor = NCBrandColor.shared.label - labelHideDownload?.text = NSLocalizedString("_share_hide_download_", comment: "") - labelHideDownload?.textColor = NCBrandColor.shared.label - labelPasswordProtect?.text = NSLocalizedString("_share_password_protect_", comment: "") - labelPasswordProtect?.textColor = NCBrandColor.shared.label - labelSetExpirationDate?.text = NSLocalizedString("_share_expiration_date_", comment: "") - labelSetExpirationDate?.textColor = NCBrandColor.shared.label - labelNoteToRecipient?.text = NSLocalizedString("_share_note_recipient_", comment: "") - labelNoteToRecipient?.textColor = NCBrandColor.shared.label - labelDeleteShareLink?.text = NSLocalizedString("_share_delete_sharelink_", comment: "") - labelDeleteShareLink?.textColor = NCBrandColor.shared.label - labelAddAnotherLink?.text = NSLocalizedString("_share_add_sharelink_", comment: "") - labelAddAnotherLink?.textColor = NCBrandColor.shared.label - - fieldSetExpirationDate.inputView = UIView() - - fieldLabel.delegate = self - fieldPasswordProtect.delegate = self - fieldNoteToRecipient.delegate = self - - imageNoteToRecipient.image = UIImage(named: "file_txt")!.image(color: NCBrandColor.shared.gray, size: 50) - imageDeleteShareLink.image = NCUtility.shared.loadImage(named: "trash", color: NCBrandColor.shared.gray, size: 50) - imageAddAnotherLink.image = NCUtility.shared.loadImage(named: "plus", color: NCBrandColor.shared.gray, size: 50) - - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) - } - - override func willMove(toWindow newWindow: UIWindow?) { - super.willMove(toWindow: newWindow) - - if newWindow == nil { - // UIView disappear - shareViewController?.reloadData() - } else { - // UIView appear - networking = NCShareNetworking(metadata: metadata!, urlBase: appDelegate.urlBase, view: self, delegate: self) - } - } - - func unLoad() { - viewWindowCalendar?.removeFromSuperview() - viewWindow?.removeFromSuperview() - - viewWindowCalendar = nil - viewWindow = nil - } - - func reloadData(idShare: Int) { - - guard let metadata = self.metadata else { return } - tableShare = NCManageDatabase.shared.getTableShare(account: metadata.account, idShare: idShare) - guard let tableShare = self.tableShare else { return } - - // Label - fieldLabel.text = tableShare.label - - if metadata.directory { - // File Drop - if tableShare.permissions == NCGlobal.shared.permissionCreateShare { - switchReadOnly.setOn(false, animated: false) - switchAllowUploadAndEditing.setOn(false, animated: false) - switchFileDrop.setOn(true, animated: false) - } else { - // Read Only - if CCUtility.isAnyPermission(toEdit: tableShare.permissions) { - switchReadOnly.setOn(false, animated: false) - switchAllowUploadAndEditing.setOn(true, animated: false) - } else { - switchReadOnly.setOn(true, animated: false) - switchAllowUploadAndEditing.setOn(false, animated: false) - } - switchFileDrop.setOn(false, animated: false) - } - } else { - // Allow editing - if CCUtility.isAnyPermission(toEdit: tableShare.permissions) { - switchAllowEditing.setOn(true, animated: false) - } else { - switchAllowEditing.setOn(false, animated: false) - } - } - - // Hide download - if tableShare.hideDownload { - switchHideDownload.setOn(true, animated: false) - } else { - switchHideDownload.setOn(false, animated: false) - } - - // Password protect - if tableShare.shareWith.count > 0 { - switchPasswordProtect.setOn(true, animated: false) - fieldPasswordProtect.isEnabled = true - fieldPasswordProtect.text = tableShare.shareWith - } else { - switchPasswordProtect.setOn(false, animated: false) - fieldPasswordProtect.isEnabled = false - fieldPasswordProtect.text = "" - } - - // Set expiration date - if tableShare.expirationDate != nil { - switchSetExpirationDate.setOn(true, animated: false) - fieldSetExpirationDate.isEnabled = true - - let dateFormatter = DateFormatter() - dateFormatter.formatterBehavior = .behavior10_4 - dateFormatter.dateStyle = .medium - fieldSetExpirationDate.text = dateFormatter.string(from: tableShare.expirationDate! as Date) - } else { - switchSetExpirationDate.setOn(false, animated: false) - fieldSetExpirationDate.isEnabled = false - fieldSetExpirationDate.text = "" - } - - // Note to recipient - fieldNoteToRecipient.text = tableShare.note - } - - func textFieldDidBeginEditing(_ textField: UITextField) { - - self.activeTextField = textField - } - - // MARK: - Keyboard notification - - @objc internal func keyboardWillShow(_ notification: Notification?) { - - activeTextfieldDiff = 0 - - if let info = notification?.userInfo, let centerObject = self.activeTextField.superview?.convert(self.activeTextField.center, to: nil) { - - let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey - if let keyboardFrame = info[frameEndUserInfoKey] as? CGRect { - let diff = keyboardFrame.origin.y - centerObject.y - self.activeTextField.frame.height - if diff < 0 { - activeTextfieldDiff = diff - self.frame.origin.y += diff - } - } - } - } - - @objc func keyboardWillHide(_ notification: Notification) { - self.frame.origin.y -= activeTextfieldDiff - } - - // MARK: - Tap viewWindowCalendar - - @objc func tapViewWindowCalendar(gesture: UITapGestureRecognizer) { - calendar?.removeFromSuperview() - viewWindowCalendar?.removeFromSuperview() - - calendar = nil - viewWindowCalendar = nil - - reloadData(idShare: tableShare?.idShare ?? 0) - } - - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - return gestureRecognizer.view == touch.view - } - - // MARK: - IBAction - - // Allow editing (file) - @IBAction func switchAllowEditingChanged(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - - var permissions: Int = 0 - - if sender.isOn { - permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: false, andIsFolder: metadata.directory) - } else { - permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: false, andIsFolder: metadata.directory) - } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Read Only (directory) - @IBAction func switchReadOnly(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - let permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: false, andIsFolder: metadata.directory) - - if sender.isOn && permissions != tableShare.permissions { - switchAllowUploadAndEditing.setOn(false, animated: false) - switchFileDrop.setOn(false, animated: false) - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } else { - sender.setOn(true, animated: false) - } - } - - // Allow Upload And Editing (directory) - @IBAction func switchAllowUploadAndEditing(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - let permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: false, andIsFolder: metadata.directory) - - if sender.isOn && permissions != tableShare.permissions { - switchReadOnly.setOn(false, animated: false) - switchFileDrop.setOn(false, animated: false) - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } else { - sender.setOn(true, animated: false) - } - } - - // File Drop (directory) - @IBAction func switchFileDrop(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - let permissions = NCGlobal.shared.permissionCreateShare - - if sender.isOn && permissions != tableShare.permissions { - switchReadOnly.setOn(false, animated: false) - switchAllowUploadAndEditing.setOn(false, animated: false) - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } else { - sender.setOn(true, animated: false) - } - } - - // Hide download - @IBAction func switchHideDownloadChanged(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: nil, hideDownload: sender.isOn) - } - - // Password protect - @IBAction func switchPasswordProtectChanged(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - - if sender.isOn { - fieldPasswordProtect.isEnabled = true - fieldPasswordProtect.text = "" - fieldPasswordProtect.becomeFirstResponder() - } else { - networking?.updateShare(idShare: tableShare.idShare, password: "", permissions: tableShare.permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - } - - @IBAction func fieldPasswordProtectDidEndOnExit(textField: UITextField) { - - guard let tableShare = self.tableShare else { return } - - networking?.updateShare(idShare: tableShare.idShare, password: fieldPasswordProtect.text, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Set expiration date - @IBAction func switchSetExpirationDate(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - - if sender.isOn { - fieldSetExpirationDate.isEnabled = true - fieldSetExpirationDate(sender: fieldSetExpirationDate) - } else { - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: "", hideDownload: tableShare.hideDownload) - } - } - - @IBAction func fieldSetExpirationDate(sender: UITextField) { - - let calendar = NCShareCommon.shared.openCalendar(view: self, width: width, height: height) - calendar.calendarView.delegate = self - self.calendar = calendar.calendarView - viewWindowCalendar = calendar.viewWindow - - let tap = UITapGestureRecognizer(target: self, action: #selector(tapViewWindowCalendar)) - tap.delegate = self - viewWindowCalendar?.addGestureRecognizer(tap) - } - - // Note to recipient - @IBAction func fieldNoteToRecipientDidEndOnExit(textField: UITextField) { - - guard let tableShare = self.tableShare else { return } - if fieldNoteToRecipient.text == nil { return } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: fieldNoteToRecipient.text, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Label - @IBAction func fielLabelDidEndOnExit(textField: UITextField) { - - guard let tableShare = self.tableShare else { return } - if fieldLabel.text == nil { return } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: fieldLabel.text, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Delete share link - @IBAction func buttonDeleteShareLink(sender: UIButton) { - - guard let tableShare = self.tableShare else { return } - - networking?.unShare(idShare: tableShare.idShare) - } - - // Add another link - @IBAction func buttonAddAnotherLink(sender: UIButton) { - - networking?.createShareLink(password: "") - } - - // MARK: - Delegate networking - - func readShareCompleted() { - reloadData(idShare: tableShare?.idShare ?? 0) - } - - func shareCompleted() { - unLoad() - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataNCShare) - } - - func unShareCompleted() { - unLoad() - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataNCShare) - } - - func updateShareWithError(idShare: Int) { - reloadData(idShare: idShare) - } - - func getSharees(sharees: [NCCommunicationSharee]?) { } - - // MARK: - Delegate calendar - - func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { - - if monthPosition == .previous || monthPosition == .next { - calendar.setCurrentPage(date, animated: true) - } else { - let dateFormatter = DateFormatter() - dateFormatter.formatterBehavior = .behavior10_4 - dateFormatter.dateStyle = .medium - fieldSetExpirationDate.text = dateFormatter.string(from: date) - fieldSetExpirationDate.endEditing(true) - - viewWindowCalendar?.removeFromSuperview() - - guard let tableShare = self.tableShare else { return } - - dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss" - let expirationDate = dateFormatter.string(from: date) - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: expirationDate, hideDownload: tableShare.hideDownload) - } - } - - func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { - return date > Date() - } - - func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - return date > Date() ? NCBrandColor.shared.label : NCBrandColor.shared.systemGray3 - } -} diff --git a/iOSClient/Share/NCShareLinkMenuView.xib b/iOSClient/Share/NCShareLinkMenuView.xib deleted file mode 100644 index 35748f2e1..000000000 --- a/iOSClient/Share/NCShareLinkMenuView.xib +++ /dev/null @@ -1,274 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina3_5" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> - <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"/> - <view opaque="NO" contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareLinkMenuView" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="250" height="500"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Placeholder" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="AjK-Ty-MRn"> - <rect key="frame" x="10" y="10" width="230" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="sVl-pt-Qe5"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fielLabelDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="WwX-ie-SRK"/> - </connections> - </textField> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="sjf-wF-y07" userLabel="SwitchAllowEditing"> - <rect key="frame" x="10" y="60" width="51" height="31"/> - <connections> - <action selector="switchAllowEditingChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="22f-fh-Qc1"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Allow editing" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHP-P8-rm2"> - <rect key="frame" x="70" y="68" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="lcS-7f-bEg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="UIn-eq-hkP"> - <rect key="frame" x="10" y="111" width="51" height="31"/> - <connections> - <action selector="switchHideDownloadChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="ZUj-2h-gQC"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hide download" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KXo-cP-gkc"> - <rect key="frame" x="70" y="119" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="IEP-cO-Um6"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="HDO-WC-RvJ"> - <rect key="frame" x="10" y="162" width="51" height="31"/> - <connections> - <action selector="switchPasswordProtectChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="wGL-du-81Q"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password protect" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HaY-OM-mQh"> - <rect key="frame" x="70" y="170" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="MeH-bs-tZK"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="e1F-PV-32s"> - <rect key="frame" x="70" y="195" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="tTv-8w-kxm"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/> - <connections> - <action selector="fieldPasswordProtectDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="2PO-wf-bHs"/> - </connections> - </textField> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="dB8-1M-WZr"> - <rect key="frame" x="10" y="245" width="51" height="31"/> - <connections> - <action selector="switchSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="26x-ld-Jsj"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Set expiration date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qll-9F-4DA"> - <rect key="frame" x="70" y="253" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="KyU-PL-PRI"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ymk-0u-ddH"> - <rect key="frame" x="70" y="278" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="G4f-LN-v7k"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="editingDidBegin" id="5Ou-k5-UM5"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="file_txt" translatesAutoresizingMaskIntoConstraints="NO" id="F4T-wQ-tBU"> - <rect key="frame" x="13" y="328" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="7uC-w2-XPl"/> - <constraint firstAttribute="width" constant="25" id="YkI-0i-Hbj"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Note to recipient" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grT-sd-j7q"> - <rect key="frame" x="70" y="333" width="175" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gof-GU-toa"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FyH-3p-EdC"> - <rect key="frame" x="70" y="358" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="hye-cf-nPD"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldNoteToRecipientDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="Z9v-qs-dp4"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="trash" translatesAutoresizingMaskIntoConstraints="NO" id="hr8-Qe-xD0" userLabel="Image Delete Share Link"> - <rect key="frame" x="13" y="408" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="Ktg-2f-87b"/> - <constraint firstAttribute="width" constant="25" id="ZJu-Y5-U67"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delete share link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ff4-JE-zGU"> - <rect key="frame" x="70" y="413" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gYi-S0-IOg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CLA-UL-mYb"> - <rect key="frame" x="13" y="408" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="fWP-XF-kQx"/> - </constraints> - <connections> - <action selector="buttonDeleteShareLinkWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="ANe-oV-NCX"/> - </connections> - </button> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="add" translatesAutoresizingMaskIntoConstraints="NO" id="6SU-Ak-Q7m"> - <rect key="frame" x="13" y="453" width="25" height="25"/> - <constraints> - <constraint firstAttribute="width" constant="25" id="NLr-qc-w3G"/> - <constraint firstAttribute="height" constant="25" id="wkp-ks-jJM"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add another link" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cna-iK-PD7"> - <rect key="frame" x="70" y="458" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="p9o-oI-gWq"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bK-Kl-hcs"> - <rect key="frame" x="13" y="453" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="wQO-6d-vWV"/> - </constraints> - <connections> - <action selector="buttonAddAnotherLinkWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="1Ea-g9-pQ8"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="0WP-PE-HTp"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="2RV-rL-sYG"/> - <constraint firstItem="HaY-OM-mQh" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="4CZ-Mo-lOP"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="centerY" secondItem="F4T-wQ-tBU" secondAttribute="centerY" id="4KH-Py-OgY"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="centerY" secondItem="dB8-1M-WZr" secondAttribute="centerY" id="5QL-7q-jdE"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="top" secondItem="grT-sd-j7q" secondAttribute="bottom" constant="10" id="7al-MO-ezA"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="8lb-ki-xfh"/> - <constraint firstItem="0bK-Kl-hcs" firstAttribute="top" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="20" id="Bka-Hj-Arm"/> - <constraint firstAttribute="trailing" secondItem="ymk-0u-ddH" secondAttribute="trailing" constant="10" id="Chd-iQ-EdR"/> - <constraint firstAttribute="trailing" secondItem="AjK-Ty-MRn" secondAttribute="trailing" constant="10" id="Cjo-Gm-Fqi"/> - <constraint firstItem="KXo-cP-gkc" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="CuB-lc-pHi"/> - <constraint firstItem="UIn-eq-hkP" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="FwN-Rj-koX"/> - <constraint firstItem="HDO-WC-RvJ" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="GTl-Tq-cLY"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="centerY" secondItem="sjf-wF-y07" secondAttribute="centerY" id="HiA-pE-L6l"/> - <constraint firstItem="6SU-Ak-Q7m" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="Hqf-wy-u3S"/> - <constraint firstAttribute="trailing" secondItem="e1F-PV-32s" secondAttribute="trailing" constant="10" id="ImT-sr-UAr"/> - <constraint firstAttribute="trailing" secondItem="0bK-Kl-hcs" secondAttribute="trailing" constant="20" id="Jy3-Qb-ucs"/> - <constraint firstAttribute="trailing" secondItem="cna-iK-PD7" secondAttribute="trailing" constant="10" id="LEz-3F-g5Q"/> - <constraint firstAttribute="trailing" secondItem="grT-sd-j7q" secondAttribute="trailing" constant="5" id="Nyn-RD-jTz"/> - <constraint firstItem="6SU-Ak-Q7m" firstAttribute="top" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="20" id="OU0-vG-YTq"/> - <constraint firstAttribute="trailing" secondItem="FyH-3p-EdC" secondAttribute="trailing" constant="10" id="RhU-wl-afT"/> - <constraint firstItem="KXo-cP-gkc" firstAttribute="centerY" secondItem="UIn-eq-hkP" secondAttribute="centerY" id="Snt-zf-mcs"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="TFC-63-muN"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="top" secondItem="e1F-PV-32s" secondAttribute="bottom" constant="20" id="TIV-5L-Cza"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="TXe-jF-DdS"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="Ua2-93-05m"/> - <constraint firstItem="HaY-OM-mQh" firstAttribute="centerY" secondItem="HDO-WC-RvJ" secondAttribute="centerY" id="UcN-gf-Prv"/> - <constraint firstItem="e1F-PV-32s" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="YCZ-Z6-1Ty"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="Zoj-Ro-jFv"/> - <constraint firstAttribute="trailing" secondItem="IHP-P8-rm2" secondAttribute="trailing" constant="10" id="Zsj-Ja-2wq"/> - <constraint firstItem="cna-iK-PD7" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="aZc-3Q-liP"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="top" secondItem="ymk-0u-ddH" secondAttribute="bottom" constant="20" id="aj8-2w-ySe"/> - <constraint firstItem="0bK-Kl-hcs" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="bSd-GG-kS3"/> - <constraint firstItem="HDO-WC-RvJ" firstAttribute="top" secondItem="UIn-eq-hkP" secondAttribute="bottom" constant="20" id="dex-mb-oRg"/> - <constraint firstItem="AjK-Ty-MRn" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="eT2-WG-2Db"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="elF-be-kqS"/> - <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="0bK-Kl-hcs" secondAttribute="bottom" constant="10" id="eyC-io-Nk6"/> - <constraint firstItem="cna-iK-PD7" firstAttribute="centerY" secondItem="0bK-Kl-hcs" secondAttribute="centerY" id="fDj-O7-Xdr"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="top" secondItem="FyH-3p-EdC" secondAttribute="bottom" constant="20" id="itX-To-Hbm"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="top" secondItem="qll-9F-4DA" secondAttribute="bottom" constant="10" id="k4G-Yb-xBy"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="lRX-gv-77N"/> - <constraint firstAttribute="trailing" secondItem="Ff4-JE-zGU" secondAttribute="trailing" constant="10" id="ljN-WF-OVS"/> - <constraint firstItem="e1F-PV-32s" firstAttribute="top" secondItem="HaY-OM-mQh" secondAttribute="bottom" constant="10" id="m5R-ms-HZh"/> - <constraint firstAttribute="trailing" secondItem="CLA-UL-mYb" secondAttribute="trailing" constant="20" id="oEb-Su-Nu5"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qEq-8J-iTD"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qrv-wQ-p6E"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="top" secondItem="AjK-Ty-MRn" secondAttribute="bottom" constant="20" id="suK-qu-fvy"/> - <constraint firstAttribute="trailing" secondItem="KXo-cP-gkc" secondAttribute="trailing" constant="10" id="uDa-6r-K84"/> - <constraint firstItem="AjK-Ty-MRn" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="vWx-aM-wFJ"/> - <constraint firstAttribute="trailing" secondItem="qll-9F-4DA" secondAttribute="trailing" constant="10" id="vaT-9Q-m84"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="x4S-GE-lJ8"/> - <constraint firstAttribute="trailing" secondItem="HaY-OM-mQh" secondAttribute="trailing" constant="10" id="yvI-xV-T7d"/> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="zc7-db-OeN"/> - <constraint firstItem="UIn-eq-hkP" firstAttribute="top" secondItem="sjf-wF-y07" secondAttribute="bottom" constant="20" id="zex-fG-9ns"/> - </constraints> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <connections> - <outlet property="buttonAddAnotherLink" destination="0bK-Kl-hcs" id="XMo-Cp-y6f"/> - <outlet property="buttonDeleteShareLink" destination="CLA-UL-mYb" id="LVP-Vh-RHb"/> - <outlet property="fieldLabel" destination="AjK-Ty-MRn" id="qar-eZ-ctz"/> - <outlet property="fieldNoteToRecipient" destination="FyH-3p-EdC" id="EBz-iA-1tq"/> - <outlet property="fieldPasswordProtect" destination="e1F-PV-32s" id="c1Y-0f-UYD"/> - <outlet property="fieldSetExpirationDate" destination="ymk-0u-ddH" id="W6o-xf-Zj2"/> - <outlet property="imageAddAnotherLink" destination="6SU-Ak-Q7m" id="Is7-q2-ZJu"/> - <outlet property="imageDeleteShareLink" destination="hr8-Qe-xD0" id="hGE-fg-rnv"/> - <outlet property="imageNoteToRecipient" destination="F4T-wQ-tBU" id="IHc-Q2-VxM"/> - <outlet property="labelAddAnotherLink" destination="cna-iK-PD7" id="xax-ip-GpR"/> - <outlet property="labelAllowEditing" destination="IHP-P8-rm2" id="bfP-WI-ryB"/> - <outlet property="labelDeleteShareLink" destination="Ff4-JE-zGU" id="7jH-iS-zf8"/> - <outlet property="labelHideDownload" destination="KXo-cP-gkc" id="vis-Zu-pga"/> - <outlet property="labelNoteToRecipient" destination="grT-sd-j7q" id="yOv-Ke-EnF"/> - <outlet property="labelPasswordProtect" destination="HaY-OM-mQh" id="nlG-u7-v1H"/> - <outlet property="labelSetExpirationDate" destination="qll-9F-4DA" id="K8B-Ys-V8e"/> - <outlet property="switchAllowEditing" destination="sjf-wF-y07" id="8J2-kj-Ugp"/> - <outlet property="switchHideDownload" destination="UIn-eq-hkP" id="25D-zI-MEj"/> - <outlet property="switchPasswordProtect" destination="HDO-WC-RvJ" id="K7a-3i-8kx"/> - <outlet property="switchSetExpirationDate" destination="dB8-1M-WZr" id="R5d-Hf-ga6"/> - </connections> - <point key="canvasLocation" x="-60" y="350.625"/> - </view> - </objects> - <resources> - <image name="add" width="25" height="25"/> - <image name="file_txt" width="300" height="300"/> - <image name="trash" width="512" height="512"/> - </resources> -</document> diff --git a/iOSClient/Share/NCShareNetworking.swift b/iOSClient/Share/NCShareNetworking.swift index 04052ec9b..cb65673c2 100644 --- a/iOSClient/Share/NCShareNetworking.swift +++ b/iOSClient/Share/NCShareNetworking.swift @@ -53,8 +53,8 @@ class NCShareNetworking: NSObject { NCUtility.shared.stopActivityIndicator() } - if errorCode == 0 && shares != nil { - NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: shares!) + if errorCode == 0, let shares = shares { + NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: shares) self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: self.metadata.account) } else { NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError) @@ -62,35 +62,27 @@ class NCShareNetworking: NSObject { self.delegate?.readShareCompleted() } } - - func createShareLink(password: String?) { - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) - let filenamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: urlBase, account: metadata.account)! - NCCommunication.shared.createShareLink(path: filenamePath, password: password) { account, share, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() - if errorCode == 0 && share != nil { - NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share!]) - self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: self.metadata.account) - } else if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError) - } - self.delegate?.shareCompleted() - } - } - func createShare(shareWith: String, shareType: Int, password: String?, metadata: tableMetadata) { + func createShare(option: NCTableShareable) { + // NOTE: Permissions don't work for creating with file drop! + // https://github.com/nextcloud/server/issues/17504 + + // NOTE: Can't save label, expirationDate, and note in same request. + // Library update needed: + // https://github.com/nextcloud/ios-communication-library/pull/104 + NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) let filenamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: urlBase, account: metadata.account)! - var permission: Int = NCManageDatabase.shared.getCapabilitiesServerInt(account: metadata.account, elements: ["ocs", "data", "capabilities", "files_sharing", "default_permissions"]) - if permission <= 0 { - permission = metadata.directory ? NCGlobal.shared.permissionMaxFolderShare : NCGlobal.shared.permissionMaxFileShare - } - NCCommunication.shared.createShare(path: filenamePath, shareType: shareType, shareWith: shareWith, password: password, permissions: permission) { (account, share, errorCode, errorDescription) in + 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() - if errorCode == 0 && share != nil { - NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share!]) + if errorCode == 0, let share = share { + option.idShare = share.idShare + NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share]) self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: self.metadata.account) + if option.hasChanges(comparedTo: share) { + self.updateShare(option: option) + } } else { NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError) } @@ -111,17 +103,17 @@ class NCShareNetworking: NSObject { } } - func updateShare(idShare: Int, password: String?, permissions: Int, note: String?, label: String?, expirationDate: String?, hideDownload: Bool) { + func updateShare(option: NCTableShareable) { NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) - NCCommunication.shared.updateShare(idShare: idShare, password: password, expireDate: expirationDate, permissions: permissions, note: note, label: label, hideDownload: hideDownload) { account, share, errorCode, errorDescription in + 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() - if errorCode == 0 && share != nil { - NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share!]) + 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) self.delegate?.readShareCompleted() } else { NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError) - self.delegate?.updateShareWithError(idShare: idShare) + self.delegate?.updateShareWithError(idShare: option.idShare) } } } diff --git a/iOSClient/Share/NCSharePaging.swift b/iOSClient/Share/NCSharePaging.swift index fdf8d59d0..c86f79b4b 100644 --- a/iOSClient/Share/NCSharePaging.swift +++ b/iOSClient/Share/NCSharePaging.swift @@ -27,14 +27,19 @@ import Parchment import NCCommunication import MarqueeLabel +protocol NCSharePagingContent { + var textField: UITextField? { get } +} + class NCSharePaging: UIViewController { private let pagingViewController = NCShareHeaderViewController() - private let appDelegate = UIApplication.shared.delegate as! AppDelegate + private weak var appDelegate = UIApplication.shared.delegate as? AppDelegate private var activityEnabled = true private var commentsEnabled = true private var sharingEnabled = true + private var currentVC: NCSharePagingContent? @objc var metadata = tableMetadata() var indexPage = NCGlobal.NCSharePagingIndex.activity @@ -47,28 +52,9 @@ class NCSharePaging: UIViewController { view.backgroundColor = NCBrandColor.shared.systemBackground navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_close_", comment: ""), style: .done, target: self, action: #selector(exitTapped)) - - // Verify Comments & Sharing enabled - let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor) - let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false) - if serverVersionMajor >= NCGlobal.shared.nextcloudVersion20 && comments == false { - commentsEnabled = false - } - let sharing = NCManageDatabase.shared.getCapabilitiesServerBool(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false) - if sharing == false { - sharingEnabled = false - } - let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesActivity) - if activity == nil { - activityEnabled = false - } - if indexPage == .sharing && !sharingEnabled { - indexPage = .activity - } - if indexPage == .activity && !activityEnabled && sharingEnabled { - indexPage = .sharing - } - + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) + setupCapabilities() // *** MUST BE THE FIRST ONE *** pagingViewController.metadata = metadata @@ -97,26 +83,53 @@ class NCSharePaging: UIViewController { // Contrain the paging view to all edges. pagingViewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - pagingViewController.view.topAnchor.constraint(equalTo: view.topAnchor), - pagingViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), - pagingViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), - pagingViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + pagingViewController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + pagingViewController.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + pagingViewController.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + pagingViewController.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor) ]) pagingViewController.dataSource = self pagingViewController.delegate = self pagingViewController.select(index: indexPage.rawValue) - let pagingIndexItem = self.pagingViewController(pagingViewController, pagingItemAt: indexPage.rawValue) as! PagingIndexItem - self.title = pagingIndexItem.title + 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() } + func setupCapabilities() { + guard let appDelegate = appDelegate else { return } + + // Verify Comments & Sharing enabled + let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor) + let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false) + if serverVersionMajor >= NCGlobal.shared.nextcloudVersion20 && comments == false { + commentsEnabled = false + } + let sharing = NCManageDatabase.shared.getCapabilitiesServerBool(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false) + sharingEnabled = sharing + let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesActivity) + activityEnabled = activity != nil + + if indexPage == .sharing && !sharingEnabled { + indexPage = .activity + } + if indexPage == .activity && !activityEnabled && sharingEnabled { + indexPage = .sharing + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + currentVC = pagingViewController.pageViewController.selectedViewController as? NCSharePagingContent + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - - if appDelegate.disableSharesView { + if appDelegate?.disableSharesView == true { self.dismiss(animated: false, completion: nil) } @@ -127,21 +140,53 @@ 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]) } - @objc func exitTapped() { - self.dismiss(animated: true, completion: nil) + deinit { + NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardDidShowNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } // MARK: - NotificationCenter + @objc func orientationDidChange() { + pagingViewController.menuItemSize = .fixed( + width: self.view.bounds.width / CGFloat(NCGlobal.NCSharePagingIndex.allCases.count), + height: 40) + currentVC?.textField?.resignFirstResponder() + } + @objc func changeTheming() { pagingViewController.indicatorColor = NCBrandColor.shared.brandElement - (pagingViewController.view as! NCSharePagingView).setupConstraints() + (pagingViewController.view as? NCSharePagingView)?.setupConstraints() pagingViewController.reloadMenu() } + + // MARK: - Keyboard & TextField + @objc func keyboardWillShow(notification: Notification) { + let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey + + guard let info = notification.userInfo, + let textField = currentVC?.textField, + let centerObject = textField.superview?.convert(textField.center, to: nil), + let keyboardFrame = info[frameEndUserInfoKey] as? CGRect + else { return } + + let diff = keyboardFrame.origin.y - centerObject.y - textField.frame.height + if diff < 0 { + view.frame.origin.y = diff + } + } + + @objc func keyboardWillHide(notification: NSNotification) { + view.frame.origin.y = 0 + } + + @objc func exitTapped() { + self.dismiss(animated: true, completion: nil) + } } // MARK: - PagingViewController Delegate @@ -155,13 +200,14 @@ extension NCSharePaging: PagingViewControllerDelegate { let itemIndex = NCGlobal.NCSharePagingIndex(rawValue: item.index) else { return } - if itemIndex == .activity && !activityEnabled { - pagingViewController.contentInteraction = .none - } else if itemIndex == .sharing && !sharingEnabled { + if itemIndex == .activity && !activityEnabled || itemIndex == .sharing && !sharingEnabled { pagingViewController.contentInteraction = .none } else { self.title = item.title } + + currentVC?.textField?.resignFirstResponder() + self.currentVC = destinationViewController as? NCSharePagingContent } } @@ -171,11 +217,13 @@ extension NCSharePaging: PagingViewControllerDataSource { func pagingViewController(_: PagingViewController, viewControllerAt index: Int) -> UIViewController { - let height = pagingViewController.options.menuHeight + NCSharePagingView.HeaderHeight + let height = pagingViewController.options.menuHeight + NCSharePagingView.headerHeight switch NCGlobal.NCSharePagingIndex(rawValue: index) { case .activity: - let viewController = UIStoryboard(name: "NCActivity", bundle: nil).instantiateInitialViewController() as! NCActivity + guard let viewController = UIStoryboard(name: "NCActivity", bundle: nil).instantiateInitialViewController() as? NCActivity else { + return UIViewController() + } viewController.height = height viewController.showComments = true viewController.didSelectItemEnable = false @@ -183,7 +231,9 @@ extension NCSharePaging: PagingViewControllerDataSource { viewController.objectType = "files" return viewController case .sharing: - let viewController = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "sharing") as! NCShare + guard let viewController = UIStoryboard(name: "NCShare", bundle: nil).instantiateViewController(withIdentifier: "sharing") as? NCShare else { + return UIViewController() + } viewController.sharingEnabled = sharingEnabled viewController.metadata = metadata viewController.height = height @@ -231,18 +281,16 @@ class NCShareHeaderViewController: PagingViewController { } override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - if NCGlobal.NCSharePagingIndex(rawValue: indexPath.item) == .activity && !activityEnabled { - return - } else if NCGlobal.NCSharePagingIndex(rawValue: indexPath.item) == .sharing && !sharingEnabled { - return - } + guard NCGlobal.NCSharePagingIndex(rawValue: indexPath.item) != .activity || activityEnabled, + NCGlobal.NCSharePagingIndex(rawValue: indexPath.item) != .sharing || sharingEnabled + else { return } super.collectionView(collectionView, didSelectItemAt: indexPath) } } class NCSharePagingView: PagingView { - static let HeaderHeight: CGFloat = 250 + static let headerHeight: CGFloat = 100 var metadata = tableMetadata() var headerHeightConstraint: NSLayoutConstraint? @@ -261,7 +309,7 @@ class NCSharePagingView: PagingView { override func setupConstraints() { - let headerView = Bundle.main.loadNibNamed("NCShareHeaderView", owner: self, options: nil)?.first as! NCShareHeaderView + guard let headerView = Bundle.main.loadNibNamed("NCShareHeaderView", owner: self, options: nil)?.first as? NCShareHeaderView else { return } headerView.backgroundColor = NCBrandColor.shared.systemBackground headerView.ocId = metadata.ocId @@ -269,9 +317,9 @@ class NCSharePagingView: PagingView { headerView.imageView.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) } else { if metadata.directory { - let image = UIImage(named: "folder")! - headerView.imageView.image = image.image(color: NCBrandColor.shared.brandElement, size: image.size.width) - } else if metadata.iconName.count > 0 { + let image = UIImage(named: "folder") + headerView.imageView.image = image?.image(color: NCBrandColor.shared.brandElement, size: image?.size.width ?? 0) + } else if !metadata.iconName.isEmpty { headerView.imageView.image = UIImage(named: metadata.iconName) } else { headerView.imageView.image = UIImage(named: "file") @@ -292,9 +340,7 @@ class NCSharePagingView: PagingView { collectionView.translatesAutoresizingMaskIntoConstraints = false headerView.translatesAutoresizingMaskIntoConstraints = false - headerHeightConstraint = headerView.heightAnchor.constraint( - equalToConstant: NCSharePagingView.HeaderHeight - ) + headerHeightConstraint = headerView.heightAnchor.constraint(equalToConstant: NCSharePagingView.headerHeight) headerHeightConstraint?.isActive = true NSLayoutConstraint.activate([ @@ -325,32 +371,26 @@ class NCShareHeaderView: UIView { override func awakeFromNib() { super.awakeFromNib() - let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longTap)) path.addGestureRecognizer(longGesture) } @IBAction func touchUpInsideFavorite(_ sender: UIButton) { - if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in - if errorCode == 0 { - if !metadata.favorite { - self.favorite.setImage(NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.yellowFavorite, size: 20), for: .normal) - } else { - self.favorite.setImage(NCUtility.shared.loadImage(named: "star.fill", color: NCBrandColor.shared.systemGray, size: 20), for: .normal) - } - } else { - NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } + NCNetworking.shared.favoriteMetadata(metadata) { errorCode, errorDescription in + if errorCode == 0 { + self.favorite.setImage(NCUtility.shared.loadImage( + named: "star.fill", + color: metadata.favorite ? NCBrandColor.shared.yellowFavorite : NCBrandColor.shared.systemGray, + size: 20), for: .normal) + } else { + NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) } } } @objc func longTap(sender: UIGestureRecognizer) { - - let board = UIPasteboard.general - board.string = path.text - + UIPasteboard.general.string = path.text NCContentPresenter.shared.messageNotification("", description: "_copied_path_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError) } } diff --git a/iOSClient/Share/NCShareQuickStatusMenu.swift b/iOSClient/Share/NCShareQuickStatusMenu.swift deleted file mode 100644 index 71d4a4be6..000000000 --- a/iOSClient/Share/NCShareQuickStatusMenu.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// NCShareQuickStatusMenu.swift -// Nextcloud -// -// Created by TSI-mc on 30/06/21. -// Copyright © 2021 Marino Faggiana. All rights reserved. -// - -import UIKit - -class NCShareQuickStatusMenu: NSObject { - - func toggleMenu(viewController: UIViewController, directory: Bool, tableShare: tableShare) { - - print(tableShare.permissions) - let menuViewController = UIStoryboard(name: "NCMenu", bundle: nil).instantiateInitialViewController() as! NCMenu - var actions = [NCMenuAction]() - - actions.append( - NCMenuAction( - title: NSLocalizedString("_share_read_only_", comment: ""), - icon: UIImage(), - selected: tableShare.permissions == (NCGlobal.shared.permissionReadShare + NCGlobal.shared.permissionShareShare) || tableShare.permissions == NCGlobal.shared.permissionReadShare, - on: false, - action: { _ in - let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) - let permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: canShare, andIsFolder: directory) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShareChangePermissions, userInfo: ["idShare": tableShare.idShare, "permissions": permissions, "hideDownload": tableShare.hideDownload]) - } - ) - ) - - actions.append( - NCMenuAction( - title: directory ? NSLocalizedString("_share_allow_upload_", comment: "") : NSLocalizedString("_share_editing_", comment: ""), - icon: UIImage(), - selected: hasUploadPermission(tableShare: tableShare), - on: false, - action: { _ in - let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) - let permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: canShare, andIsFolder: directory) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShareChangePermissions, userInfo: ["idShare": tableShare.idShare, "permissions": permissions, "hideDownload": tableShare.hideDownload]) - } - ) - ) - - menuViewController.actions = actions - - let menuPanelController = NCMenuPanelController() - menuPanelController.parentPresenter = viewController - menuPanelController.delegate = menuViewController - menuPanelController.set(contentViewController: menuViewController) - menuPanelController.track(scrollView: menuViewController.tableView) - - viewController.present(menuPanelController, animated: true, completion: nil) - } - - fileprivate func hasUploadPermission(tableShare: tableShare) -> Bool { - let uploadPermissions = [ - NCGlobal.shared.permissionMaxFileShare, - NCGlobal.shared.permissionMaxFolderShare, - NCGlobal.shared.permissionDefaultFileRemoteShareNoSupportShareOption, - NCGlobal.shared.permissionDefaultFolderRemoteShareNoSupportShareOption] - return uploadPermissions.contains(tableShare.permissions) - } -} diff --git a/iOSClient/Share/NCShareUserCell.swift b/iOSClient/Share/NCShareUserCell.swift index c20fe3dc0..97ef503b2 100644 --- a/iOSClient/Share/NCShareUserCell.swift +++ b/iOSClient/Share/NCShareUserCell.swift @@ -22,6 +22,7 @@ import UIKit import DropDown +import NCCommunication class NCShareUserCell: UITableViewCell, NCCellProtocol { @@ -37,24 +38,54 @@ class NCShareUserCell: UITableViewCell, NCCellProtocol { var tableShare: tableShare? weak var delegate: NCShareUserCellDelegate? - var fileAvatarImageView: UIImageView? { - get { - return imageItem - } - } - var fileObjectId: String? { - get { - return nil + var fileAvatarImageView: UIImageView? { return imageItem } + var fileObjectId: String? { return nil } + var filePreviewImageView: UIImageView? { return nil } + var fileUser: String? { return tableShare?.shareWith } + + func setupCellUI(userId: String) { + guard let tableShare = tableShare else { + return } - } - var filePreviewImageView: UIImageView? { - get { - return nil + self.accessibilityCustomActions = [UIAccessibilityCustomAction( + name: NSLocalizedString("_show_profile_", comment: ""), + target: self, + selector: #selector(tapAvatarImage))] + + labelTitle.text = tableShare.shareWithDisplayname + labelTitle.textColor = NCBrandColor.shared.label + isUserInteractionEnabled = true + labelQuickStatus.isHidden = false + imageDownArrow.isHidden = false + buttonMenu.isHidden = false + buttonMenu.accessibilityLabel = NSLocalizedString("_more_", comment: "") + imageItem.image = NCShareCommon.shared.getImageShareType(shareType: tableShare.shareType) + + let status = NCUtility.shared.getUserStatus(userIcon: tableShare.userIcon, userStatus: tableShare.userStatus, userMessage: tableShare.userMessage) + imageStatus.image = status.onlineStatus + self.status.text = status.statusMessage + + // If the initiator or the recipient is not the current user, show the list of sharees without any options to edit it. + if tableShare.uidOwner != userId && tableShare.uidFileOwner != userId { + isUserInteractionEnabled = false + labelQuickStatus.isHidden = true + imageDownArrow.isHidden = true + buttonMenu.isHidden = true } - } - var fileUser: String? { - get { - return tableShare?.shareWith + + btnQuickStatus.accessibilityHint = NSLocalizedString("_user_sharee_footer_", comment: "") + btnQuickStatus.setTitle("", for: .normal) + btnQuickStatus.contentHorizontalAlignment = .left + + if tableShare.permissions == NCGlobal.shared.permissionCreateShare { + labelQuickStatus.text = NSLocalizedString("_share_file_drop_", comment: "") + } else { + // Read Only + if CCUtility.isAnyPermission(toEdit: tableShare.permissions) { + labelQuickStatus.text = NSLocalizedString("_share_editing_", comment: "") + } else { + labelQuickStatus.text = NSLocalizedString("_share_read_only_", comment: "") + } } } @@ -63,7 +94,7 @@ class NCShareUserCell: UITableViewCell, NCCellProtocol { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapAvatarImage)) imageItem?.addGestureRecognizer(tapGesture) - buttonMenu.setImage(UIImage(named: "shareMenu")!.image(color: .gray, size: 50), for: .normal) + buttonMenu.setImage(UIImage(named: "shareMenu")?.image(color: .gray, size: 50), for: .normal) labelQuickStatus.textColor = NCBrandColor.shared.customer imageDownArrow.image = NCUtility.shared.loadImage(named: "arrowtriangle.down.fill", color: NCBrandColor.shared.customer) } @@ -87,9 +118,9 @@ protocol NCShareUserCellDelegate: AnyObject { func quickStatus(with tableShare: tableShare?, sender: Any) } -// MARK: - NCShareUserDropDownCell +// MARK: - NCSearchUserDropDownCell -class NCShareUserDropDownCell: DropDownCell, NCCellProtocol { +class NCSearchUserDropDownCell: DropDownCell, NCCellProtocol { @IBOutlet weak var imageItem: UIImageView! @IBOutlet weak var imageStatus: UIImageView! @@ -99,21 +130,9 @@ class NCShareUserDropDownCell: DropDownCell, NCCellProtocol { private var user: String = "" - var fileAvatarImageView: UIImageView? { - get { - return imageItem - } - } - var fileObjectId: String? { - get { - return nil - } - } - var filePreviewImageView: UIImageView? { - get { - return nil - } - } + var fileAvatarImageView: UIImageView? { return imageItem } + var fileObjectId: String? { return nil } + var filePreviewImageView: UIImageView? { return nil } var fileUser: String? { get { return user @@ -122,4 +141,44 @@ class NCShareUserDropDownCell: DropDownCell, NCCellProtocol { user = newValue ?? "" } } + + func setupCell(sharee: NCCommunicationSharee, baseUrl: NCUserBaseUrl) { + imageItem.image = NCShareCommon.shared.getImageShareType(shareType: sharee.shareType) + imageShareeType.image = NCShareCommon.shared.getImageShareType(shareType: sharee.shareType) + let status = NCUtility.shared.getUserStatus(userIcon: sharee.userIcon, userStatus: sharee.userStatus, userMessage: sharee.userMessage) + imageStatus.image = status.onlineStatus + self.status.text = status.statusMessage + if self.status.text?.count ?? 0 > 0 { + centerTitle.constant = -5 + } else { + centerTitle.constant = 0 + } + + imageItem.image = NCUtility.shared.loadUserImage( + for: sharee.shareWith, + displayName: nil, + userBaseUrl: baseUrl) + + let fileName = baseUrl.userBaseUrl + "-" + sharee.shareWith + ".png" + if NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) == nil { + let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName + let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag + + NCCommunication.shared.downloadAvatar( + user: sharee.shareWith, + fileNameLocalPath: fileNameLocalPath, + sizeImage: NCGlobal.shared.avatarSize, + avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, + etag: etag) { _, imageAvatar, _, etag, errorCode, _ in + + if errorCode == 0, let etag = etag, let imageAvatar = imageAvatar { + NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) + self.imageItem.image = imageAvatar + } else if errorCode == NCGlobal.shared.errorNotModified, let imageAvatar = NCManageDatabase.shared.setAvatarLoaded(fileName: fileName) { + self.imageItem.image = imageAvatar + } + } + } + + } } diff --git a/iOSClient/Share/NCShareUserFolderMenuView.xib b/iOSClient/Share/NCShareUserFolderMenuView.xib deleted file mode 100644 index 2908ef2a1..000000000 --- a/iOSClient/Share/NCShareUserFolderMenuView.xib +++ /dev/null @@ -1,226 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina3_5" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> - <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"/> - <view opaque="NO" contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareUserMenuView" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="250" height="420"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="sjf-wF-y07"> - <rect key="frame" x="10" y="10" width="51" height="31"/> - <connections> - <action selector="switchCanReshareChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="Ezn-AP-uEh"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Can reshare" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHP-P8-rm2"> - <rect key="frame" x="70" y="18" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="lcS-7f-bEg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="dB8-1M-WZr"> - <rect key="frame" x="10" y="214" width="51" height="31"/> - <connections> - <action selector="switchSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="A9c-YF-bXd"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Set expiration date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qll-9F-4DA"> - <rect key="frame" x="70" y="222" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="KyU-PL-PRI"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ymk-0u-ddH"> - <rect key="frame" x="70" y="247" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="G4f-LN-v7k"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="WdF-Ie-Di0"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="file_txt" translatesAutoresizingMaskIntoConstraints="NO" id="F4T-wQ-tBU"> - <rect key="frame" x="13" y="297" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="7uC-w2-XPl"/> - <constraint firstAttribute="width" constant="25" id="YkI-0i-Hbj"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Note to recipient" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grT-sd-j7q"> - <rect key="frame" x="70" y="302" width="175" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gof-GU-toa"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FyH-3p-EdC"> - <rect key="frame" x="70" y="327" width="170" height="34"/> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldNoteToRecipientDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="q1P-u7-EBw"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="trash" translatesAutoresizingMaskIntoConstraints="NO" id="hr8-Qe-xD0" userLabel="Image Delete Share Link"> - <rect key="frame" x="13" y="381" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="Ktg-2f-87b"/> - <constraint firstAttribute="width" constant="25" id="ZJu-Y5-U67"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Unshare" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ff4-JE-zGU"> - <rect key="frame" x="70" y="386" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gYi-S0-IOg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CLA-UL-mYb"> - <rect key="frame" x="13" y="381" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="fWP-XF-kQx"/> - </constraints> - <connections> - <action selector="buttonUnshareWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="Nky-nT-rCz"/> - </connections> - </button> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="1br-PL-gFf"> - <rect key="frame" x="10" y="61" width="51" height="31"/> - <connections> - <action selector="switchCanCreateWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="M2Z-bc-ZKU"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Can create" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G13-DS-7uC"> - <rect key="frame" x="70" y="69" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="nK7-xW-fbq"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="0yc-df-C1f"> - <rect key="frame" x="10" y="112" width="51" height="31"/> - <connections> - <action selector="switchCanChangeWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="XIQ-GD-IU7"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Can change" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YWL-Ks-51c"> - <rect key="frame" x="70" y="120" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="NYy-3u-P1R"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="vja-ge-I6S"> - <rect key="frame" x="10" y="163" width="51" height="31"/> - <connections> - <action selector="switchCanDeleteWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="BRV-e3-C17"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Can delete" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dZP-fH-9sg"> - <rect key="frame" x="70" y="171" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="p6u-3B-cFa"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - </subviews> - <constraints> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="0WP-PE-HTp"/> - <constraint firstItem="YWL-Ks-51c" firstAttribute="leading" secondItem="0yc-df-C1f" secondAttribute="trailing" constant="11" id="18d-Lv-xE7"/> - <constraint firstItem="dZP-fH-9sg" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="2N4-ol-qeH"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="2RV-rL-sYG"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="centerY" secondItem="F4T-wQ-tBU" secondAttribute="centerY" id="4KH-Py-OgY"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="centerY" secondItem="dB8-1M-WZr" secondAttribute="centerY" id="5QL-7q-jdE"/> - <constraint firstItem="vja-ge-I6S" firstAttribute="top" secondItem="0yc-df-C1f" secondAttribute="bottom" constant="20" id="6jy-3A-HWD"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="top" secondItem="grT-sd-j7q" secondAttribute="bottom" constant="10" id="7al-MO-ezA"/> - <constraint firstItem="dZP-fH-9sg" firstAttribute="centerY" secondItem="vja-ge-I6S" secondAttribute="centerY" id="7ne-dy-Lt1"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="8lb-ki-xfh"/> - <constraint firstAttribute="trailing" secondItem="ymk-0u-ddH" secondAttribute="trailing" constant="10" id="Chd-iQ-EdR"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="EW6-D3-tml"/> - <constraint firstItem="0yc-df-C1f" firstAttribute="top" secondItem="1br-PL-gFf" secondAttribute="bottom" constant="20" id="FGV-mm-Ko5"/> - <constraint firstItem="G13-DS-7uC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="GqL-Dy-FyM"/> - <constraint firstItem="1br-PL-gFf" firstAttribute="top" secondItem="sjf-wF-y07" secondAttribute="bottom" constant="20" id="HCV-Fc-QqK"/> - <constraint firstAttribute="trailing" secondItem="YWL-Ks-51c" secondAttribute="trailing" constant="10" id="HHo-s3-88y"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="centerY" secondItem="sjf-wF-y07" secondAttribute="centerY" id="HiA-pE-L6l"/> - <constraint firstItem="1br-PL-gFf" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="JKb-Jd-pOz"/> - <constraint firstItem="0yc-df-C1f" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="JYX-ih-uiD"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="top" secondItem="vja-ge-I6S" secondAttribute="bottom" constant="20" id="NSb-s1-gmD"/> - <constraint firstAttribute="trailing" secondItem="grT-sd-j7q" secondAttribute="trailing" constant="5" id="Nyn-RD-jTz"/> - <constraint firstAttribute="trailing" secondItem="FyH-3p-EdC" secondAttribute="trailing" constant="10" id="RhU-wl-afT"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="TFC-63-muN"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="TXe-jF-DdS"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="Ua2-93-05m"/> - <constraint firstAttribute="trailing" secondItem="dZP-fH-9sg" secondAttribute="trailing" constant="10" id="Y3X-Bg-zXC"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="Zoj-Ro-jFv"/> - <constraint firstAttribute="trailing" secondItem="IHP-P8-rm2" secondAttribute="trailing" constant="10" id="Zsj-Ja-2wq"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="top" secondItem="ymk-0u-ddH" secondAttribute="bottom" constant="20" id="aj8-2w-ySe"/> - <constraint firstItem="G13-DS-7uC" firstAttribute="centerY" secondItem="1br-PL-gFf" secondAttribute="centerY" id="cbx-hu-mhg"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="elF-be-kqS"/> - <constraint firstAttribute="trailing" secondItem="G13-DS-7uC" secondAttribute="trailing" constant="10" id="gKS-GP-xkX"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="top" secondItem="FyH-3p-EdC" secondAttribute="bottom" constant="20" id="itX-To-Hbm"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="top" secondItem="qll-9F-4DA" secondAttribute="bottom" constant="10" id="k4G-Yb-xBy"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="lRX-gv-77N"/> - <constraint firstAttribute="trailing" secondItem="Ff4-JE-zGU" secondAttribute="trailing" constant="10" id="ljN-WF-OVS"/> - <constraint firstAttribute="trailing" secondItem="CLA-UL-mYb" secondAttribute="trailing" constant="20" id="oEb-Su-Nu5"/> - <constraint firstItem="YWL-Ks-51c" firstAttribute="centerY" secondItem="0yc-df-C1f" secondAttribute="centerY" id="p2G-NK-Qcr"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qEq-8J-iTD"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qrv-wQ-p6E"/> - <constraint firstItem="vja-ge-I6S" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="ukl-JN-XFD"/> - <constraint firstAttribute="trailing" secondItem="qll-9F-4DA" secondAttribute="trailing" constant="10" id="vaT-9Q-m84"/> - <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="10" id="w6T-Q9-OKA"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="x4S-GE-lJ8"/> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="zc7-db-OeN"/> - </constraints> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <connections> - <outlet property="buttonUnshare" destination="CLA-UL-mYb" id="fwq-pr-JO0"/> - <outlet property="fieldNoteToRecipient" destination="FyH-3p-EdC" id="389-TM-dhC"/> - <outlet property="fieldSetExpirationDate" destination="ymk-0u-ddH" id="erm-BP-BWD"/> - <outlet property="imageNoteToRecipient" destination="F4T-wQ-tBU" id="hv7-Ln-aYs"/> - <outlet property="imageUnshare" destination="hr8-Qe-xD0" id="kfC-D4-Ak0"/> - <outlet property="labelCanChange" destination="YWL-Ks-51c" id="3lh-TV-rKt"/> - <outlet property="labelCanCreate" destination="G13-DS-7uC" id="BOR-I2-EVX"/> - <outlet property="labelCanDelete" destination="dZP-fH-9sg" id="iCn-VY-P3p"/> - <outlet property="labelCanReshare" destination="IHP-P8-rm2" id="dkZ-O3-1cB"/> - <outlet property="labelNoteToRecipient" destination="grT-sd-j7q" id="0Rj-H1-Bqv"/> - <outlet property="labelSetExpirationDate" destination="qll-9F-4DA" id="SsD-jd-FX1"/> - <outlet property="labelUnshare" destination="Ff4-JE-zGU" id="Ubq-EL-yOd"/> - <outlet property="switchCanChange" destination="0yc-df-C1f" id="JcH-18-ZRX"/> - <outlet property="switchCanCreate" destination="1br-PL-gFf" id="3En-bH-Wzm"/> - <outlet property="switchCanDelete" destination="vja-ge-I6S" id="NN7-2l-NSz"/> - <outlet property="switchCanReshare" destination="sjf-wF-y07" id="3jS-Gr-YMT"/> - <outlet property="switchSetExpirationDate" destination="dB8-1M-WZr" id="0Ki-ah-3FE"/> - </connections> - <point key="canvasLocation" x="1.875" y="195"/> - </view> - </objects> - <resources> - <image name="file_txt" width="300" height="300"/> - <image name="trash" width="512" height="512"/> - </resources> -</document> diff --git a/iOSClient/Share/NCShareUserMenuView.swift b/iOSClient/Share/NCShareUserMenuView.swift deleted file mode 100644 index 9dd2c2be2..000000000 --- a/iOSClient/Share/NCShareUserMenuView.swift +++ /dev/null @@ -1,398 +0,0 @@ -// -// NCShareUserMenuView.swift -// Nextcloud -// -// Created by Marino Faggiana on 25/07/2019. -// Copyright © 2019 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 FSCalendar -import NCCommunication - -class NCShareUserMenuView: UIView, UIGestureRecognizerDelegate, UITextFieldDelegate, NCShareNetworkingDelegate, FSCalendarDelegate, FSCalendarDelegateAppearance { - - @IBOutlet weak var switchCanReshare: UISwitch! - @IBOutlet weak var labelCanReshare: UILabel! - - @IBOutlet weak var switchCanCreate: UISwitch! - @IBOutlet weak var labelCanCreate: UILabel! - - @IBOutlet weak var switchCanChange: UISwitch! - @IBOutlet weak var labelCanChange: UILabel! - - @IBOutlet weak var switchCanDelete: UISwitch! - @IBOutlet weak var labelCanDelete: UILabel! - - @IBOutlet weak var switchSetExpirationDate: UISwitch! - @IBOutlet weak var labelSetExpirationDate: UILabel! - @IBOutlet weak var fieldSetExpirationDate: UITextField! - - @IBOutlet weak var imageNoteToRecipient: UIImageView! - @IBOutlet weak var labelNoteToRecipient: UILabel! - @IBOutlet weak var fieldNoteToRecipient: UITextField! - - @IBOutlet weak var buttonUnshare: UIButton! - @IBOutlet weak var labelUnshare: UILabel! - @IBOutlet weak var imageUnshare: UIImageView! - - private let appDelegate = UIApplication.shared.delegate as! AppDelegate - - var width: CGFloat = 0 - var height: CGFloat = 0 - - private var tableShare: tableShare? - var metadata: tableMetadata? - var shareViewController: NCShare? - private var networking: NCShareNetworking? - - var viewWindow: UIView? - var viewWindowCalendar: UIView? - private var calendar: FSCalendar? - private var activeTextfieldDiff: CGFloat = 0 - private var activeTextField = UITextField() - - override func awakeFromNib() { - - layer.borderColor = UIColor.lightGray.cgColor - layer.borderWidth = 0.5 - layer.cornerRadius = 5 - layer.masksToBounds = false - layer.shadowOffset = CGSize(width: 2, height: 2) - layer.shadowOpacity = 0.2 - - switchCanReshare.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchCanReshare.onTintColor = NCBrandColor.shared.brandElement - switchCanCreate?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchCanCreate?.onTintColor = NCBrandColor.shared.brandElement - switchCanChange?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchCanChange?.onTintColor = NCBrandColor.shared.brandElement - switchCanDelete?.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchCanDelete?.onTintColor = NCBrandColor.shared.brandElement - switchSetExpirationDate.transform = CGAffineTransform(scaleX: 0.75, y: 0.75) - switchSetExpirationDate.onTintColor = NCBrandColor.shared.brandElement - - labelCanReshare?.text = NSLocalizedString("_share_can_reshare_", comment: "") - labelCanReshare?.textColor = NCBrandColor.shared.label - labelCanCreate?.text = NSLocalizedString("_share_can_create_", comment: "") - labelCanCreate?.textColor = NCBrandColor.shared.label - labelCanChange?.text = NSLocalizedString("_share_can_change_", comment: "") - labelCanChange?.textColor = NCBrandColor.shared.label - labelCanDelete?.text = NSLocalizedString("_share_can_delete_", comment: "") - labelCanDelete?.textColor = NCBrandColor.shared.label - labelSetExpirationDate?.text = NSLocalizedString("_share_expiration_date_", comment: "") - labelSetExpirationDate?.textColor = NCBrandColor.shared.label - labelNoteToRecipient?.text = NSLocalizedString("_share_note_recipient_", comment: "") - labelNoteToRecipient?.textColor = NCBrandColor.shared.label - labelUnshare?.text = NSLocalizedString("_share_unshare_", comment: "") - labelUnshare?.textColor = NCBrandColor.shared.label - - fieldSetExpirationDate.inputView = UIView() - - fieldNoteToRecipient.delegate = self - - imageNoteToRecipient.image = UIImage(named: "file_txt")!.image(color: NCBrandColor.shared.gray, size: 50) - imageUnshare.image = NCUtility.shared.loadImage(named: "trash", color: NCBrandColor.shared.gray, size: 50) - - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) - } - - override func willMove(toWindow newWindow: UIWindow?) { - super.willMove(toWindow: newWindow) - - if newWindow == nil { - // UIView disappear - shareViewController?.reloadData() - } else { - // UIView appear - networking = NCShareNetworking(metadata: metadata!, urlBase: appDelegate.urlBase, view: self, delegate: self) - } - } - - func unLoad() { - viewWindowCalendar?.removeFromSuperview() - viewWindow?.removeFromSuperview() - - viewWindowCalendar = nil - viewWindow = nil - } - - func reloadData(idShare: Int) { - - guard let metadata = self.metadata else { return } - tableShare = NCManageDatabase.shared.getTableShare(account: metadata.account, idShare: idShare) - guard let tableShare = self.tableShare else { return } - - // Can reshare (file) - let canReshare = CCUtility.isPermission(toCanShare: tableShare.permissions) - switchCanReshare.setOn(canReshare, animated: false) - - if metadata.directory { - // Can create (folder) - let canCreate = CCUtility.isPermission(toCanCreate: tableShare.permissions) - switchCanCreate.setOn(canCreate, animated: false) - - // Can change (folder) - let canChange = CCUtility.isPermission(toCanChange: tableShare.permissions) - switchCanChange.setOn(canChange, animated: false) - - // Can delete (folder) - let canDelete = CCUtility.isPermission(toCanDelete: tableShare.permissions) - switchCanDelete.setOn(canDelete, animated: false) - } - - // Set expiration date - if tableShare.expirationDate != nil { - switchSetExpirationDate.setOn(true, animated: false) - fieldSetExpirationDate.isEnabled = true - - let dateFormatter = DateFormatter() - dateFormatter.formatterBehavior = .behavior10_4 - dateFormatter.dateStyle = .medium - fieldSetExpirationDate.text = dateFormatter.string(from: tableShare.expirationDate! as Date) - } else { - switchSetExpirationDate.setOn(false, animated: false) - fieldSetExpirationDate.isEnabled = false - fieldSetExpirationDate.text = "" - } - - // Note to recipient - fieldNoteToRecipient.text = tableShare.note - } - - func textFieldDidBeginEditing(_ textField: UITextField) { - - self.activeTextField = textField - } - - // MARK: - Keyboard notification - - @objc internal func keyboardWillShow(_ notification: Notification?) { - - activeTextfieldDiff = 0 - - if let info = notification?.userInfo, let centerObject = self.activeTextField.superview?.convert(self.activeTextField.center, to: nil) { - - let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey - if let keyboardFrame = info[frameEndUserInfoKey] as? CGRect { - let diff = keyboardFrame.origin.y - centerObject.y - self.activeTextField.frame.height - if diff < 0 { - activeTextfieldDiff = diff - self.frame.origin.y += diff - } - } - } - } - - @objc func keyboardWillHide(_ notification: Notification) { - self.frame.origin.y -= activeTextfieldDiff - } - - // MARK: - Tap viewWindowCalendar - - @objc func tapViewWindowCalendar(gesture: UITapGestureRecognizer) { - calendar?.removeFromSuperview() - viewWindowCalendar?.removeFromSuperview() - - calendar = nil - viewWindowCalendar = nil - - reloadData(idShare: tableShare?.idShare ?? 0) - } - - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - return gestureRecognizer.view == touch.view - } - - // MARK: - IBAction - - // Can reshare - @IBAction func switchCanReshareChanged(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - - let canEdit = CCUtility.isAnyPermission(toEdit: tableShare.permissions) - let canCreate = CCUtility.isPermission(toCanCreate: tableShare.permissions) - let canChange = CCUtility.isPermission(toCanChange: tableShare.permissions) - let canDelete = CCUtility.isPermission(toCanDelete: tableShare.permissions) - - var permissions: Int = 0 - - if metadata.directory { - permissions = CCUtility.getPermissionsValue(byCanEdit: canEdit, andCanCreate: canCreate, andCanChange: canChange, andCanDelete: canDelete, andCanShare: sender.isOn, andIsFolder: metadata.directory) - } else { - if sender.isOn { - if canEdit { - permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: sender.isOn, andIsFolder: metadata.directory) - } else { - permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: sender.isOn, andIsFolder: metadata.directory) - } - } else { - if canEdit { - permissions = CCUtility.getPermissionsValue(byCanEdit: true, andCanCreate: true, andCanChange: true, andCanDelete: true, andCanShare: sender.isOn, andIsFolder: metadata.directory) - } else { - permissions = CCUtility.getPermissionsValue(byCanEdit: false, andCanCreate: false, andCanChange: false, andCanDelete: false, andCanShare: sender.isOn, andIsFolder: metadata.directory) - } - } - } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - @IBAction func switchCanCreate(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - - let canEdit = CCUtility.isAnyPermission(toEdit: tableShare.permissions) - let canChange = CCUtility.isPermission(toCanChange: tableShare.permissions) - let canDelete = CCUtility.isPermission(toCanDelete: tableShare.permissions) - let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) - - let permissions = CCUtility.getPermissionsValue(byCanEdit: canEdit, andCanCreate: sender.isOn, andCanChange: canChange, andCanDelete: canDelete, andCanShare: canShare, andIsFolder: metadata.directory) - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - @IBAction func switchCanChange(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - - let canEdit = CCUtility.isAnyPermission(toEdit: tableShare.permissions) - let canCreate = CCUtility.isPermission(toCanCreate: tableShare.permissions) - let canDelete = CCUtility.isPermission(toCanDelete: tableShare.permissions) - let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) - - let permissions = CCUtility.getPermissionsValue(byCanEdit: canEdit, andCanCreate: canCreate, andCanChange: sender.isOn, andCanDelete: canDelete, andCanShare: canShare, andIsFolder: metadata.directory) - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - @IBAction func switchCanDelete(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - guard let metadata = self.metadata else { return } - - let canEdit = CCUtility.isAnyPermission(toEdit: tableShare.permissions) - let canCreate = CCUtility.isPermission(toCanCreate: tableShare.permissions) - let canChange = CCUtility.isPermission(toCanChange: tableShare.permissions) - let canShare = CCUtility.isPermission(toCanShare: tableShare.permissions) - - let permissions = CCUtility.getPermissionsValue(byCanEdit: canEdit, andCanCreate: canCreate, andCanChange: canChange, andCanDelete: sender.isOn, andCanShare: canShare, andIsFolder: metadata.directory) - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: permissions, note: nil, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Set expiration date - @IBAction func switchSetExpirationDate(sender: UISwitch) { - - guard let tableShare = self.tableShare else { return } - - if sender.isOn { - fieldSetExpirationDate.isEnabled = true - fieldSetExpirationDate(sender: fieldSetExpirationDate) - } else { - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: "", hideDownload: tableShare.hideDownload) - } - } - - @IBAction func fieldSetExpirationDate(sender: UITextField) { - - let calendar = NCShareCommon.shared.openCalendar(view: self, width: width, height: height) - calendar.calendarView.delegate = self - self.calendar = calendar.calendarView - viewWindowCalendar = calendar.viewWindow - - let tap = UITapGestureRecognizer(target: self, action: #selector(tapViewWindowCalendar)) - tap.delegate = self - viewWindowCalendar?.addGestureRecognizer(tap) - } - - // Note to recipient - @IBAction func fieldNoteToRecipientDidEndOnExit(textField: UITextField) { - - guard let tableShare = self.tableShare else { return } - if fieldNoteToRecipient.text == nil { return } - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: fieldNoteToRecipient.text, label: nil, expirationDate: nil, hideDownload: tableShare.hideDownload) - } - - // Unshare - @IBAction func buttonUnshare(sender: UIButton) { - - guard let tableShare = self.tableShare else { return } - - networking?.unShare(idShare: tableShare.idShare) - } - - // MARK: - Delegate networking - - func readShareCompleted() { - reloadData(idShare: tableShare?.idShare ?? 0) - } - - func shareCompleted() { - unLoad() - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataNCShare) - } - - func unShareCompleted() { - unLoad() - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataNCShare) - } - - func updateShareWithError(idShare: Int) { - reloadData(idShare: idShare) - } - - func getSharees(sharees: [NCCommunicationSharee]?) { } - - // MARK: - Delegate calendar - - func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { - - if monthPosition == .previous || monthPosition == .next { - calendar.setCurrentPage(date, animated: true) - } else { - let dateFormatter = DateFormatter() - dateFormatter.formatterBehavior = .behavior10_4 - dateFormatter.dateStyle = .medium - fieldSetExpirationDate.text = dateFormatter.string(from: date) - fieldSetExpirationDate.endEditing(true) - - viewWindowCalendar?.removeFromSuperview() - - guard let tableShare = self.tableShare else { return } - - dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss" - let expirationDate = dateFormatter.string(from: date) - - networking?.updateShare(idShare: tableShare.idShare, password: nil, permissions: tableShare.permissions, note: nil, label: nil, expirationDate: expirationDate, hideDownload: tableShare.hideDownload) - } - } - - func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { - return date > Date() - } - - func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - return date > Date() ? NCBrandColor.shared.label : NCBrandColor.shared.systemGray3 - } -} diff --git a/iOSClient/Share/NCShareUserMenuView.xib b/iOSClient/Share/NCShareUserMenuView.xib deleted file mode 100644 index 38a4304ad..000000000 --- a/iOSClient/Share/NCShareUserMenuView.xib +++ /dev/null @@ -1,160 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19455" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina3_5" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19454"/> - <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"/> - <view opaque="NO" contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCShareUserMenuView" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="250" height="270"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="sjf-wF-y07"> - <rect key="frame" x="10" y="10" width="51" height="31"/> - <connections> - <action selector="switchCanReshareChangedWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="Ezn-AP-uEh"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Can reshare" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IHP-P8-rm2"> - <rect key="frame" x="70" y="18" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="lcS-7f-bEg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="dB8-1M-WZr"> - <rect key="frame" x="10" y="61" width="51" height="31"/> - <connections> - <action selector="switchSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="valueChanged" id="A9c-YF-bXd"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Set expiration date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qll-9F-4DA"> - <rect key="frame" x="70" y="69" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="KyU-PL-PRI"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="ymk-0u-ddH"> - <rect key="frame" x="70" y="94" width="170" height="30"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="G4f-LN-v7k"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldSetExpirationDateWithSender:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="WdF-Ie-Di0"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="file_txt" translatesAutoresizingMaskIntoConstraints="NO" id="F4T-wQ-tBU"> - <rect key="frame" x="13" y="144" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="7uC-w2-XPl"/> - <constraint firstAttribute="width" constant="25" id="YkI-0i-Hbj"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Note to recipient" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grT-sd-j7q"> - <rect key="frame" x="70" y="149" width="175" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gof-GU-toa"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FyH-3p-EdC"> - <rect key="frame" x="70" y="174" width="170" height="34"/> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <textInputTraits key="textInputTraits"/> - <connections> - <action selector="fieldNoteToRecipientDidEndOnExitWithTextField:" destination="iN0-l3-epB" eventType="editingDidEndOnExit" id="q1P-u7-EBw"/> - </connections> - </textField> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="trash" translatesAutoresizingMaskIntoConstraints="NO" id="hr8-Qe-xD0" userLabel="Image Delete Share Link"> - <rect key="frame" x="13" y="228" width="25" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="Ktg-2f-87b"/> - <constraint firstAttribute="width" constant="25" id="ZJu-Y5-U67"/> - </constraints> - </imageView> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Unshare" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ff4-JE-zGU"> - <rect key="frame" x="70" y="233" width="170" height="15"/> - <constraints> - <constraint firstAttribute="height" constant="15" id="gYi-S0-IOg"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CLA-UL-mYb"> - <rect key="frame" x="13" y="228" width="217" height="25"/> - <constraints> - <constraint firstAttribute="height" constant="25" id="fWP-XF-kQx"/> - </constraints> - <connections> - <action selector="buttonUnshareWithSender:" destination="iN0-l3-epB" eventType="touchUpInside" id="Nky-nT-rCz"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="0WP-PE-HTp"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="2RV-rL-sYG"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="centerY" secondItem="F4T-wQ-tBU" secondAttribute="centerY" id="4KH-Py-OgY"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="centerY" secondItem="dB8-1M-WZr" secondAttribute="centerY" id="5QL-7q-jdE"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="top" secondItem="grT-sd-j7q" secondAttribute="bottom" constant="10" id="7al-MO-ezA"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="8lb-ki-xfh"/> - <constraint firstAttribute="trailing" secondItem="ymk-0u-ddH" secondAttribute="trailing" constant="10" id="Chd-iQ-EdR"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="EW6-D3-tml"/> - <constraint firstItem="IHP-P8-rm2" firstAttribute="centerY" secondItem="sjf-wF-y07" secondAttribute="centerY" id="HiA-pE-L6l"/> - <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="CLA-UL-mYb" secondAttribute="bottom" constant="10" id="MQ9-xT-wSR"/> - <constraint firstAttribute="trailing" secondItem="grT-sd-j7q" secondAttribute="trailing" constant="5" id="Nyn-RD-jTz"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="top" secondItem="sjf-wF-y07" secondAttribute="bottom" constant="20" id="P2C-Pq-hSl"/> - <constraint firstAttribute="trailing" secondItem="FyH-3p-EdC" secondAttribute="trailing" constant="10" id="RhU-wl-afT"/> - <constraint firstItem="sjf-wF-y07" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="TFC-63-muN"/> - <constraint firstItem="FyH-3p-EdC" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="TXe-jF-DdS"/> - <constraint firstItem="dB8-1M-WZr" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="10" id="Ua2-93-05m"/> - <constraint firstItem="CLA-UL-mYb" firstAttribute="centerY" secondItem="hr8-Qe-xD0" secondAttribute="centerY" id="Zoj-Ro-jFv"/> - <constraint firstAttribute="trailing" secondItem="IHP-P8-rm2" secondAttribute="trailing" constant="10" id="Zsj-Ja-2wq"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="top" secondItem="ymk-0u-ddH" secondAttribute="bottom" constant="20" id="aj8-2w-ySe"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="elF-be-kqS"/> - <constraint firstItem="hr8-Qe-xD0" firstAttribute="top" secondItem="FyH-3p-EdC" secondAttribute="bottom" constant="20" id="itX-To-Hbm"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="top" secondItem="qll-9F-4DA" secondAttribute="bottom" constant="10" id="k4G-Yb-xBy"/> - <constraint firstItem="grT-sd-j7q" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="lRX-gv-77N"/> - <constraint firstAttribute="trailing" secondItem="Ff4-JE-zGU" secondAttribute="trailing" constant="10" id="ljN-WF-OVS"/> - <constraint firstAttribute="trailing" secondItem="CLA-UL-mYb" secondAttribute="trailing" constant="20" id="oEb-Su-Nu5"/> - <constraint firstItem="qll-9F-4DA" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qEq-8J-iTD"/> - <constraint firstItem="ymk-0u-ddH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="qrv-wQ-p6E"/> - <constraint firstAttribute="trailing" secondItem="qll-9F-4DA" secondAttribute="trailing" constant="10" id="vaT-9Q-m84"/> - <constraint firstItem="F4T-wQ-tBU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="x4S-GE-lJ8"/> - <constraint firstItem="Ff4-JE-zGU" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="70" id="zc7-db-OeN"/> - </constraints> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <connections> - <outlet property="buttonUnshare" destination="CLA-UL-mYb" id="fwq-pr-JO0"/> - <outlet property="fieldNoteToRecipient" destination="FyH-3p-EdC" id="389-TM-dhC"/> - <outlet property="fieldSetExpirationDate" destination="ymk-0u-ddH" id="erm-BP-BWD"/> - <outlet property="imageNoteToRecipient" destination="F4T-wQ-tBU" id="hv7-Ln-aYs"/> - <outlet property="imageUnshare" destination="hr8-Qe-xD0" id="kfC-D4-Ak0"/> - <outlet property="labelCanReshare" destination="IHP-P8-rm2" id="dkZ-O3-1cB"/> - <outlet property="labelNoteToRecipient" destination="grT-sd-j7q" id="0Rj-H1-Bqv"/> - <outlet property="labelSetExpirationDate" destination="qll-9F-4DA" id="SsD-jd-FX1"/> - <outlet property="labelUnshare" destination="Ff4-JE-zGU" id="Ubq-EL-yOd"/> - <outlet property="switchCanReshare" destination="sjf-wF-y07" id="3jS-Gr-YMT"/> - <outlet property="switchSetExpirationDate" destination="dB8-1M-WZr" id="0Ki-ah-3FE"/> - </connections> - <point key="canvasLocation" x="2" y="196"/> - </view> - </objects> - <resources> - <image name="file_txt" width="300" height="300"/> - <image name="trash" width="512" height="512"/> - </resources> -</document> diff --git a/iOSClient/Supporting Files/af.lproj/InfoPlist.strings b/iOSClient/Supporting Files/af.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/af.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/af.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/af.lproj/Localizable.strings b/iOSClient/Supporting Files/af.lproj/Localizable.strings Binary files differindex 8af22bc37..38666ba61 100644 --- a/iOSClient/Supporting Files/af.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/af.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ar.lproj/InfoPlist.strings b/iOSClient/Supporting Files/ar.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ar.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ar.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ar.lproj/Localizable.strings b/iOSClient/Supporting Files/ar.lproj/Localizable.strings Binary files differindex f1ac2903e..70d6407c0 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/InfoPlist.strings b/iOSClient/Supporting Files/ast.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ast.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ast.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ast.lproj/Localizable.strings b/iOSClient/Supporting Files/ast.lproj/Localizable.strings Binary files differindex 09a842f36..96059f72a 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/InfoPlist.strings b/iOSClient/Supporting Files/az.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/az.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/az.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/az.lproj/Localizable.strings b/iOSClient/Supporting Files/az.lproj/Localizable.strings Binary files differindex d7647967f..dd3846eeb 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/InfoPlist.strings b/iOSClient/Supporting Files/be.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/be.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/be.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/be.lproj/Localizable.strings b/iOSClient/Supporting Files/be.lproj/Localizable.strings Binary files differindex 61aa73400..0c89fcb1e 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/InfoPlist.strings b/iOSClient/Supporting Files/bg_BG.lproj/InfoPlist.strings Binary files differindex b907f6e78..e93a4a9cd 100644 --- a/iOSClient/Supporting Files/bg_BG.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/bg_BG.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings b/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings Binary files differindex ea077df63..c1f1efe32 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/InfoPlist.strings b/iOSClient/Supporting Files/bn_BD.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/bn_BD.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/bn_BD.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings b/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings Binary files differindex 25c719311..58546a3cc 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/InfoPlist.strings b/iOSClient/Supporting Files/br.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/br.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/br.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/br.lproj/Localizable.strings b/iOSClient/Supporting Files/br.lproj/Localizable.strings Binary files differindex 236b4c10a..ed158d553 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/InfoPlist.strings b/iOSClient/Supporting Files/bs.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/bs.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/bs.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/bs.lproj/Localizable.strings b/iOSClient/Supporting Files/bs.lproj/Localizable.strings Binary files differindex afdb00d49..09e98ca41 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/InfoPlist.strings b/iOSClient/Supporting Files/ca.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ca.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ca.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ca.lproj/Localizable.strings b/iOSClient/Supporting Files/ca.lproj/Localizable.strings Binary files differindex 5bd2b1379..4678b2456 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 93032bd6a..a345c34e5 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/InfoPlist.strings b/iOSClient/Supporting Files/cy_GB.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/cy_GB.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/cy_GB.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings b/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings Binary files differindex c38de2e6a..479794262 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/InfoPlist.strings b/iOSClient/Supporting Files/da.lproj/InfoPlist.strings Binary files differindex 466a99c4a..9fe347b0d 100644 --- a/iOSClient/Supporting Files/da.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/da.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/da.lproj/Localizable.strings b/iOSClient/Supporting Files/da.lproj/Localizable.strings Binary files differindex ddce71e31..4715b9ce4 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 ea3634aa7..849fecc6b 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/InfoPlist.strings b/iOSClient/Supporting Files/el.lproj/InfoPlist.strings Binary files differindex b907f6e78..7c5a3231f 100644 --- a/iOSClient/Supporting Files/el.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/el.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/el.lproj/Localizable.strings b/iOSClient/Supporting Files/el.lproj/Localizable.strings Binary files differindex 5b845b08c..3d34ca6c3 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/InfoPlist.strings b/iOSClient/Supporting Files/en-GB.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/en-GB.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/en-GB.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings Binary files differindex 33332d7aa..e8e417484 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/InfoPlist.strings b/iOSClient/Supporting Files/en.lproj/InfoPlist.strings index e163dea69..353940953 100644 --- a/iOSClient/Supporting Files/en.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/en.lproj/InfoPlist.strings @@ -1,7 +1,7 @@ NSCameraUsageDescription = "Camera access is required to scan documents and make photo and video."; NSFaceIDUsageDescription = "Face ID is required to authenticate using face recognition."; -NSLocationAlwaysUsageDescription = "GPS is used to detect new photos from camera roll, continued use of GPS running in the background can dramatically decrease battery life."; +NSLocationAlwaysUsageDescription = "GPS is used to detect new photos from camera roll. Continued use of GPS running in the background can dramatically decrease battery life."; NSPhotoLibraryUsageDescription = "Photo library access is required to upload your photos and videos to your cloud."; NSPhotoLibraryAddUsageDescription = "Photo library access is required to upload your photos and videos to your cloud."; NSMicrophoneUsageDescription = "Microphone access is required to create voice notes."; -NSLocationWhenInUseUsageDescription = "GPS is used to detect new photos from camera roll on background, the use of GPS only when the App is in use is useless."; +NSLocationWhenInUseUsageDescription = "GPS is used to detect new photos from camera roll on background. It is useless to use GPS only while using the app."; diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index 49952614a..cb7ccaeae 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -22,8 +22,8 @@ "_cancel_" = "Cancel"; "_tap_to_cancel_" = "Tap to cancel"; +"_cancel_request_" = "Do you want to cancel?"; "_upload_file_" = "Upload file"; -"_accessibility_add_upload_" = "Add and upload"; "_download_file_" = "Download file"; "_loading_" = "Loading"; "_loading_with_points_" = "Loading …"; @@ -62,6 +62,7 @@ "_beta_version_" = "Beta version"; "_function_in_testing_" = "Function in testing, please send information about any problems you run into."; "_done_" = "Done"; +"_clear_" = "Clear"; "_passcode_too_short_" = "Passcode too short, at least 4 characters required"; "_selected_" = "Selected"; "_scan_fingerprint_" = "Scan fingerprint to authenticate"; @@ -81,6 +82,7 @@ "_remove_" = "Remove"; "_file_not_found_" = "File not found"; "_continue_" = "Continue"; +"_continue_editing_" = "Continue editing"; "_continue_request_" = "Do you want to continue?"; "_auto_upload_folder_" = "Auto upload"; "_gallery_" = "Gallery"; @@ -92,7 +94,7 @@ "_initialization_" = "Initialization"; "_experimental_" = "Experimental"; "_select_dir_media_tab_" = "Select as folder \"Media\""; -"_error_creation_file_" = "Ops! Could not create the file"; +"_error_creation_file_" = "Oops! Could not create the file"; "_save_path_" = "Save path"; "_save_settings_" = "Save settings"; "_mode_filename_" = "Filename mode"; @@ -145,6 +147,15 @@ "_view_in_folder_" = "View in folder"; "_leave_share_" = "Leave this share"; +/* MARK: Files lock */ + +"_lock_file_" = "Lock file"; +"_unlock_file_" = "Unlock file"; +"_lock_selected_files_" = "Lock files"; +"_unlock_selected_files_" = "Unlock files"; +"_locked_by_" = "Locked by %@"; +"_file_locked_no_override_" = "This file is locked. It cannot be overridden."; + /* Remove a file from a list, don't delete it entirely */ "_remove_file_" = "Remove file"; @@ -204,6 +215,8 @@ "_default_color_" = "Use the default color"; "_as_default_color_" = "Use as default color"; +// MARK: User Status + /* User status */ "_online_" = "Online"; @@ -254,6 +267,7 @@ "_file_already_exists_" = "Unable to complete the operation, a file with the same name exists"; "_read_file_error_" = "Could not read the file"; "_write_file_error_" = "Could not write the file"; +"_files_lock_error_" = "There was an error changing the lock of this file"; "_more_" = "More"; "_notifications_" = "Notifications"; "_logout_" = "Log out"; @@ -336,10 +350,12 @@ "_user_editprofile_" = "Edit profile"; "_select_offline_warning_" = "Making multiple files and folders available offline may take a while and use a lot of memory while doing so."; "_advanced_" = "Advanced"; +"_permissions_" = "Permissions"; "_disable_files_app_" = "Disable Files App integration"; "_disable_files_app_footer_" = "Do not permit the access of files via the iOS Files application"; "_trial_" = "Trial"; "_trial_expired_day_" = "Days remaining"; +"_time_remaining_" = "%@ remaining"; "_disableLocalCacheAfterUpload_footer_" = "After uploading the file, do not keep it in the local cache"; "_disableLocalCacheAfterUpload_" = "Disable local cache"; "_autoupload_" = "Auto upload photos/videos"; @@ -426,7 +442,7 @@ "_error_createsubfolders_upload_" = "Error creating subfolders"; "_activate_autoupload_" = "Enable auto upload"; "_remove_photo_CameraRoll_" = "Remove from camera roll"; -"_remove_photo_CameraRoll_desc_" = "After successful automatic uploads, a confirmation message will be displayed to delete the uploaded photos or videos from the camera roll. The deleted photos or videos will still be available in the iOS Photos Trash for 30 days."; +"_remove_photo_CameraRoll_desc_" = "\"Remove from camera roll\" after uploads, a confirmation message will be displayed to delete the uploaded photos or videos from the camera roll. The deleted photos or videos will still be available in the iOS Photos Trash for 30 days."; "_never_" = "never"; "_less_a_minute_" = "less than a minute ago"; "_a_minute_ago_" = "a minute ago"; @@ -562,9 +578,12 @@ "_insert_password_pfd_" = "Secured PDF. Enter password"; "_password_pdf_error_" = "Wrong password"; "_error_download_photobrowser_" = "Error: Unable to download photo"; + +// MARK: Share + "_share_link_" = "Share link"; "_share_link_button_" = "Send link to …"; -"_Link_name_" = "Link name"; +"_share_link_name_" = "Link name"; "_password_" = "Password"; "_share_password_" = "Password protected link"; "_share_expirationdate_" = "Set expiration date for link"; @@ -609,6 +628,9 @@ "_share_unshare_" = "Unshare"; "_share_internal_link_" = "Internal link"; "_share_internal_link_des_" = "Only works for users with access to this folder"; +"_share_reshare_disabled_" = "You are not allowed to reshare this file/folder"; +"_share_reshare_restricted_" = "Note: You only have limited permission to reshare this file/folder"; + "_no_transfer_" = "No transfers yet"; "_no_transfer_sub_" = "Uploads and downloads from this device will show up here"; "_no_activity_" = "No activity yet"; @@ -722,8 +744,8 @@ "_go_online_" = "Go online to see the document"; "_intro_1_title_" = "Keep your data secure and under your control"; "_intro_2_title_" = "Secure collaboration & file exchange"; -"_intro_3_title_" = "Easy-to-use web mail, calendering & contacts"; -"_intro_4_title_" = "Screensharing, online meetings & web conferences"; +"_intro_3_title_" = "Easy-to-use web mail, calendaring & contacts"; +"_intro_4_title_" = "Screen sharing, online meetings & web conferences"; "_log_in_" = "Log in"; "_sign_up_" = "Sign up with provider"; "_host_your_own_server" = "Host your own server"; @@ -803,7 +825,8 @@ "_overwrite_original_" = "Overwrite original"; "_save_as_copy_" = "Save as copy"; "_discard_changes_" = "Close and discard changes"; -"_message_disable_overwrite_livephoto_" = "This image it's a Live Photo, the overwrite will not possible"; +"_message_disable_overwrite_livephoto_" = "This image is a Live Photo, overwrite will not be possible"; +"_discard_changes_info_" = "Your changes will be discarded."; "_delete_files_desc_" = "Delete files to free up space"; "_delete_old_files_" = "Delete all files older than"; "_never_" = "Never"; @@ -830,7 +853,9 @@ "_certificates_" = "Certificates"; "_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?"; // Video +"_select_trace_" = "Select the trace"; "_video_processing_" = "Video processing"; "_video_being_processed_" = "Video being processed …"; "_downloading_" = "Downloading"; @@ -841,4 +866,26 @@ "_stay_app_foreground_" = "Keep the app in the foreground …"; "_conversion_available_" = "The conversion is always available on menu"; "_video_format_not_recognized_" = "This video needs to be processed to be played, do you want to do it now?"; +"_video_must_download_" = "This video needs to be downloaded and processed to be played, do you want to do it now?"; "_conversion_max_compatibility_" = "Max compatibility, the conversion can take much longer"; +"_video_tap_for_close_" = "A slight pressure to close the processing"; +"_subtitle_not_found_" = "Subtitle not found"; +"_disable_" = "Disable"; +"_subtitle_not_dowloaded_" = "There are subtitles not downloaded locally"; + +// Tip +"_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails."; + +// MARK: Accessibility + +// Accessibility, floating panel top element +"_cart_controller_" = "Card controller"; +"_accessibility_add_upload_" = "Add and upload"; +"_dismiss_menu_" = "Dismiss the menu"; +"_show_profile_" = "Show profile"; +// a11y: On/Off +"_on_" = "On"; +// a11y: On/Off +"_off_" = "Off"; +"_grid_view_" = "Show grid view"; +"_list_view_" = "Show list view"; diff --git a/iOSClient/Supporting Files/eo.lproj/InfoPlist.strings b/iOSClient/Supporting Files/eo.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/eo.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/eo.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/eo.lproj/Localizable.strings b/iOSClient/Supporting Files/eo.lproj/Localizable.strings Binary files differindex e752850f3..0999863cc 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/InfoPlist.strings b/iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-419.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings Binary files differindex 08e4b7142..f0d8dd7af 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/InfoPlist.strings b/iOSClient/Supporting Files/es-AR.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-AR.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-AR.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings Binary files differindex 4d2b401be..e9222dd44 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/InfoPlist.strings b/iOSClient/Supporting Files/es-CL.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-CL.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-CL.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings Binary files differindex 1df3a142a..7a52551e5 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/InfoPlist.strings b/iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-CO.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings Binary files differindex 1df3a142a..fd097fa4f 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/InfoPlist.strings b/iOSClient/Supporting Files/es-CR.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-CR.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-CR.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings Binary files differindex 1df3a142a..1f9cfde75 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/InfoPlist.strings b/iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-DO.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings Binary files differindex 1df3a142a..fcff56a2c 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/InfoPlist.strings b/iOSClient/Supporting Files/es-EC.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-EC.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-EC.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings Binary files differindex 999010d18..6e787565a 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/InfoPlist.strings b/iOSClient/Supporting Files/es-GT.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-GT.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-GT.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings Binary files differindex 1df3a142a..1f9cfde75 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/InfoPlist.strings b/iOSClient/Supporting Files/es-HN.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-HN.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-HN.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings b/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings Binary files differindex 682a10fc4..34a8f1c0d 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/InfoPlist.strings b/iOSClient/Supporting Files/es-MX.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-MX.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-MX.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings b/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings Binary files differindex 06042e9b1..29ff32f9d 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/InfoPlist.strings b/iOSClient/Supporting Files/es-NI.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-NI.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-NI.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings b/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings Binary files differindex 682a10fc4..aa61e2455 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/InfoPlist.strings b/iOSClient/Supporting Files/es-PA.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-PA.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-PA.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings Binary files differindex 682a10fc4..aa61e2455 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/InfoPlist.strings b/iOSClient/Supporting Files/es-PE.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-PE.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-PE.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings Binary files differindex 682a10fc4..aa61e2455 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/InfoPlist.strings b/iOSClient/Supporting Files/es-PR.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-PR.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-PR.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings Binary files differindex 682a10fc4..aa61e2455 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/InfoPlist.strings b/iOSClient/Supporting Files/es-PY.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-PY.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-PY.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings Binary files differindex 69c760b42..484370411 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/InfoPlist.strings b/iOSClient/Supporting Files/es-SV.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-SV.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-SV.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings b/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings Binary files differindex 1df3a142a..1f9cfde75 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/InfoPlist.strings b/iOSClient/Supporting Files/es-UY.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/es-UY.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es-UY.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings Binary files differindex 682a10fc4..ba9fc8776 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/InfoPlist.strings b/iOSClient/Supporting Files/es.lproj/InfoPlist.strings Binary files differindex 7b744e241..0be5276e3 100644 --- a/iOSClient/Supporting Files/es.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/es.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/es.lproj/Localizable.strings b/iOSClient/Supporting Files/es.lproj/Localizable.strings Binary files differindex d4811b624..5ba66b02e 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/InfoPlist.strings b/iOSClient/Supporting Files/et_EE.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/et_EE.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/et_EE.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings Binary files differindex 39381572f..618b9fd1a 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/InfoPlist.strings b/iOSClient/Supporting Files/eu.lproj/InfoPlist.strings Binary files differindex b907f6e78..6db2ffca3 100644 --- a/iOSClient/Supporting Files/eu.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/eu.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/eu.lproj/Localizable.strings b/iOSClient/Supporting Files/eu.lproj/Localizable.strings Binary files differindex e5c3566c8..ed6b156bd 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/InfoPlist.strings b/iOSClient/Supporting Files/fa.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/fa.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/fa.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/fa.lproj/Localizable.strings b/iOSClient/Supporting Files/fa.lproj/Localizable.strings Binary files differindex f6f6b67b9..020b7b6bc 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/InfoPlist.strings b/iOSClient/Supporting Files/fi-FI.lproj/InfoPlist.strings Binary files differindex b907f6e78..17ad11ec9 100644 --- a/iOSClient/Supporting Files/fi-FI.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/fi-FI.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings b/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings Binary files differindex 4f3846b4c..b0fa858c0 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/InfoPlist.strings b/iOSClient/Supporting Files/fo.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/fo.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/fo.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/fo.lproj/Localizable.strings b/iOSClient/Supporting Files/fo.lproj/Localizable.strings Binary files differindex cad2830f0..aa76ef0b0 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 dabb43b5e..ad8c81d8e 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 adb4b7364..f847542a1 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/InfoPlist.strings b/iOSClient/Supporting Files/gd.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/gd.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/gd.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/gd.lproj/Localizable.strings b/iOSClient/Supporting Files/gd.lproj/Localizable.strings Binary files differindex 29465f2a1..2e758e91c 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/InfoPlist.strings b/iOSClient/Supporting Files/gl.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/gl.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/gl.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/gl.lproj/Localizable.strings b/iOSClient/Supporting Files/gl.lproj/Localizable.strings Binary files differindex 860cfba88..53b4ba168 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/InfoPlist.strings b/iOSClient/Supporting Files/he.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/he.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/he.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/he.lproj/Localizable.strings b/iOSClient/Supporting Files/he.lproj/Localizable.strings Binary files differindex bca57ad9e..a09013332 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/InfoPlist.strings b/iOSClient/Supporting Files/hi_IN.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/hi_IN.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/hi_IN.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings Binary files differindex 29465f2a1..d6feb161f 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/InfoPlist.strings b/iOSClient/Supporting Files/hr.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/hr.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/hr.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/hr.lproj/Localizable.strings b/iOSClient/Supporting Files/hr.lproj/Localizable.strings Binary files differindex f9b8306b3..0e5ef62ad 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/InfoPlist.strings b/iOSClient/Supporting Files/hsb.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/hsb.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/hsb.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/hsb.lproj/Localizable.strings b/iOSClient/Supporting Files/hsb.lproj/Localizable.strings Binary files differindex 1ce567a41..6f11a3e51 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/InfoPlist.strings b/iOSClient/Supporting Files/hu.lproj/InfoPlist.strings Binary files differindex 1a9745288..1add04cfe 100644 --- a/iOSClient/Supporting Files/hu.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/hu.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/hu.lproj/Localizable.strings b/iOSClient/Supporting Files/hu.lproj/Localizable.strings Binary files differindex b32adfb13..d78522d67 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/InfoPlist.strings b/iOSClient/Supporting Files/hy.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/hy.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/hy.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/hy.lproj/Localizable.strings b/iOSClient/Supporting Files/hy.lproj/Localizable.strings Binary files differindex 688f0e548..a56cf377e 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/InfoPlist.strings b/iOSClient/Supporting Files/ia.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ia.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ia.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ia.lproj/Localizable.strings b/iOSClient/Supporting Files/ia.lproj/Localizable.strings Binary files differindex 50a2d5f91..4447846b0 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/InfoPlist.strings b/iOSClient/Supporting Files/id.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/id.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/id.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/id.lproj/Localizable.strings b/iOSClient/Supporting Files/id.lproj/Localizable.strings Binary files differindex abffe4686..2b5c81e56 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/InfoPlist.strings b/iOSClient/Supporting Files/ig.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ig.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ig.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ig.lproj/Localizable.strings b/iOSClient/Supporting Files/ig.lproj/Localizable.strings Binary files differindex 29465f2a1..b1e6f9f2f 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/InfoPlist.strings b/iOSClient/Supporting Files/is.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/is.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/is.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/is.lproj/Localizable.strings b/iOSClient/Supporting Files/is.lproj/Localizable.strings Binary files differindex 1f42be7e8..e99e2bb2d 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/InfoPlist.strings b/iOSClient/Supporting Files/it.lproj/InfoPlist.strings Binary files differindex 9f957bc21..acd43ce85 100644 --- a/iOSClient/Supporting Files/it.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/it.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/it.lproj/Localizable.strings b/iOSClient/Supporting Files/it.lproj/Localizable.strings Binary files differindex a35b9bf43..8e113f338 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/InfoPlist.strings b/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings Binary files differindex b907f6e78..ebd63f78d 100644 --- a/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings Binary files differindex 785646f12..4c5ef5480 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/InfoPlist.strings b/iOSClient/Supporting Files/ka-GE.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ka-GE.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ka-GE.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings b/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings Binary files differindex 8711f456d..384abecfc 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/InfoPlist.strings b/iOSClient/Supporting Files/ka.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ka.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ka.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ka.lproj/Localizable.strings b/iOSClient/Supporting Files/ka.lproj/Localizable.strings Binary files differindex d31ff4616..800fbe66e 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/InfoPlist.strings b/iOSClient/Supporting Files/kab.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/kab.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/kab.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/kab.lproj/Localizable.strings b/iOSClient/Supporting Files/kab.lproj/Localizable.strings Binary files differindex 7b125ea57..83f45f735 100644 --- a/iOSClient/Supporting Files/kab.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/kab.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings b/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/kk.lproj/Localizable.strings b/iOSClient/Supporting Files/kk.lproj/Localizable.strings Binary files differindex 29465f2a1..d6feb161f 100644 --- a/iOSClient/Supporting Files/kk.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/kk.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/km.lproj/InfoPlist.strings b/iOSClient/Supporting Files/km.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/km.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/km.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/km.lproj/Localizable.strings b/iOSClient/Supporting Files/km.lproj/Localizable.strings Binary files differindex a48abd3c0..e2343fd71 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/InfoPlist.strings b/iOSClient/Supporting Files/kn.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/kn.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/kn.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/kn.lproj/Localizable.strings b/iOSClient/Supporting Files/kn.lproj/Localizable.strings Binary files differindex a848f6d5e..105d64240 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/InfoPlist.strings b/iOSClient/Supporting Files/ko.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ko.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ko.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ko.lproj/Localizable.strings b/iOSClient/Supporting Files/ko.lproj/Localizable.strings Binary files differindex ac7a42d2c..6c9f2609d 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/InfoPlist.strings b/iOSClient/Supporting Files/la.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/la.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/la.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/la.lproj/Localizable.strings b/iOSClient/Supporting Files/la.lproj/Localizable.strings Binary files differindex 91c1683d2..2baa2f06e 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/InfoPlist.strings b/iOSClient/Supporting Files/lb.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/lb.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/lb.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/lb.lproj/Localizable.strings b/iOSClient/Supporting Files/lb.lproj/Localizable.strings Binary files differindex b27bbd24b..4990dc5c8 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/InfoPlist.strings b/iOSClient/Supporting Files/lo.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/lo.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/lo.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/lo.lproj/Localizable.strings b/iOSClient/Supporting Files/lo.lproj/Localizable.strings Binary files differindex 8432bea32..150df0194 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/InfoPlist.strings b/iOSClient/Supporting Files/lt_LT.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/lt_LT.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/lt_LT.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings Binary files differindex a347fc39b..cba973880 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/InfoPlist.strings b/iOSClient/Supporting Files/lv.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/lv.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/lv.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/lv.lproj/Localizable.strings b/iOSClient/Supporting Files/lv.lproj/Localizable.strings Binary files differindex 3e525055c..80d3d3063 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/InfoPlist.strings b/iOSClient/Supporting Files/mk.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/mk.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/mk.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/mk.lproj/Localizable.strings b/iOSClient/Supporting Files/mk.lproj/Localizable.strings Binary files differindex 6937fb8db..79abd4af6 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/InfoPlist.strings b/iOSClient/Supporting Files/mn.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/mn.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/mn.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/mn.lproj/Localizable.strings b/iOSClient/Supporting Files/mn.lproj/Localizable.strings Binary files differindex b389828d7..2106f082d 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/InfoPlist.strings b/iOSClient/Supporting Files/mr.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/mr.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/mr.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/mr.lproj/Localizable.strings b/iOSClient/Supporting Files/mr.lproj/Localizable.strings Binary files differindex 29465f2a1..9a4c98b34 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/InfoPlist.strings b/iOSClient/Supporting Files/ms_MY.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ms_MY.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ms_MY.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings Binary files differindex 602303db4..a68b50a6b 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/InfoPlist.strings b/iOSClient/Supporting Files/my.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/my.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/my.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/my.lproj/Localizable.strings b/iOSClient/Supporting Files/my.lproj/Localizable.strings Binary files differindex cb25a9ecc..29dec98a8 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/InfoPlist.strings b/iOSClient/Supporting Files/nb-NO.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/nb-NO.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/nb-NO.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings Binary files differindex a616388f0..95fcaa666 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/InfoPlist.strings b/iOSClient/Supporting Files/ne.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ne.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ne.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ne.lproj/Localizable.strings b/iOSClient/Supporting Files/ne.lproj/Localizable.strings Binary files differindex 0837b531d..e882cb781 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/InfoPlist.strings b/iOSClient/Supporting Files/nl.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/nl.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/nl.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/nl.lproj/Localizable.strings b/iOSClient/Supporting Files/nl.lproj/Localizable.strings Binary files differindex 6587eaffb..9b6b823d5 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/InfoPlist.strings b/iOSClient/Supporting Files/nn_NO.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/nn_NO.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/nn_NO.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings Binary files differindex e8654611a..1f04dcda5 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/InfoPlist.strings b/iOSClient/Supporting Files/oc.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/oc.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/oc.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/oc.lproj/Localizable.strings b/iOSClient/Supporting Files/oc.lproj/Localizable.strings Binary files differindex ec06d8816..e04570e48 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/InfoPlist.strings b/iOSClient/Supporting Files/pl.lproj/InfoPlist.strings Binary files differindex b055470d7..d630826b2 100644 --- a/iOSClient/Supporting Files/pl.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/pl.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/pl.lproj/Localizable.strings b/iOSClient/Supporting Files/pl.lproj/Localizable.strings Binary files differindex 500e7296d..e1065e025 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/InfoPlist.strings b/iOSClient/Supporting Files/ps.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ps.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ps.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ps.lproj/Localizable.strings b/iOSClient/Supporting Files/ps.lproj/Localizable.strings Binary files differindex 6ed383774..9031888a4 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/InfoPlist.strings b/iOSClient/Supporting Files/pt-BR.lproj/InfoPlist.strings Binary files differindex d73155d57..b0013897e 100644 --- a/iOSClient/Supporting Files/pt-BR.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/pt-BR.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings Binary files differindex 74336c384..464d3f855 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 b907f6e78..b58e1cf05 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 26c2f511d..0e826616a 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/InfoPlist.strings b/iOSClient/Supporting Files/ro.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ro.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ro.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ro.lproj/Localizable.strings b/iOSClient/Supporting Files/ro.lproj/Localizable.strings Binary files differindex 41de44c52..fe1d77757 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 b907f6e78..b58e1cf05 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 2b40acab0..c87d9f964 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/InfoPlist.strings b/iOSClient/Supporting Files/sc.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/sc.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sc.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sc.lproj/Localizable.strings b/iOSClient/Supporting Files/sc.lproj/Localizable.strings Binary files differindex 04f9753f0..43da9b89e 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/InfoPlist.strings b/iOSClient/Supporting Files/si.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/si.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/si.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/si.lproj/Localizable.strings b/iOSClient/Supporting Files/si.lproj/Localizable.strings Binary files differindex e3ef30803..ca90225b0 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 4afaa09dc..d0a95dc1c 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 b26ecfca3..389d16f42 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 b907f6e78..b58e1cf05 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 561ab7d2f..e93456fc3 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/InfoPlist.strings b/iOSClient/Supporting Files/sq.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/sq.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sq.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sq.lproj/Localizable.strings b/iOSClient/Supporting Files/sq.lproj/Localizable.strings Binary files differindex 4db0730ea..8e1194c04 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/InfoPlist.strings b/iOSClient/Supporting Files/sr.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/sr.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sr.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sr.lproj/Localizable.strings b/iOSClient/Supporting Files/sr.lproj/Localizable.strings Binary files differindex ce3c29b44..63b2a1bea 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/InfoPlist.strings b/iOSClient/Supporting Files/sr@latin.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/sr@latin.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sr@latin.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings Binary files differindex 551b3d8e9..e18103b5c 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/InfoPlist.strings b/iOSClient/Supporting Files/sv.lproj/InfoPlist.strings Binary files differindex 1edb2895e..94cb1cc84 100644 --- a/iOSClient/Supporting Files/sv.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sv.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sv.lproj/Localizable.strings b/iOSClient/Supporting Files/sv.lproj/Localizable.strings Binary files differindex 2e693b521..be2b8a132 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/InfoPlist.strings b/iOSClient/Supporting Files/sw.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/sw.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sw.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sw.lproj/Localizable.strings b/iOSClient/Supporting Files/sw.lproj/Localizable.strings Binary files differindex 29465f2a1..d6feb161f 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/InfoPlist.strings b/iOSClient/Supporting Files/ta.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ta.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ta.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ta.lproj/Localizable.strings b/iOSClient/Supporting Files/ta.lproj/Localizable.strings Binary files differindex 77655b07c..bfb871e98 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/InfoPlist.strings b/iOSClient/Supporting Files/th_TH.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/th_TH.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/th_TH.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings Binary files differindex 43b01cf13..16615f2b1 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/InfoPlist.strings b/iOSClient/Supporting Files/tk.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/tk.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/tk.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/tk.lproj/Localizable.strings b/iOSClient/Supporting Files/tk.lproj/Localizable.strings Binary files differindex 5b1131787..be672326e 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/InfoPlist.strings b/iOSClient/Supporting Files/tr.lproj/InfoPlist.strings Binary files differindex a4424f682..3d6e79102 100644 --- a/iOSClient/Supporting Files/tr.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/tr.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/tr.lproj/Localizable.strings b/iOSClient/Supporting Files/tr.lproj/Localizable.strings Binary files differindex 9abfe1c5d..fdff9602b 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/InfoPlist.strings b/iOSClient/Supporting Files/ug.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ug.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ug.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ug.lproj/Localizable.strings b/iOSClient/Supporting Files/ug.lproj/Localizable.strings Binary files differindex ad7e9d073..4fd0b3de0 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/InfoPlist.strings b/iOSClient/Supporting Files/uk.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/uk.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/uk.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/uk.lproj/Localizable.strings b/iOSClient/Supporting Files/uk.lproj/Localizable.strings Binary files differindex d5c5277f1..c0ecb041e 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/InfoPlist.strings b/iOSClient/Supporting Files/ur_PK.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/ur_PK.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ur_PK.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings b/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings Binary files differindex 3912758b2..2be0c7a1a 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/InfoPlist.strings b/iOSClient/Supporting Files/uz.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/uz.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/uz.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/uz.lproj/Localizable.strings b/iOSClient/Supporting Files/uz.lproj/Localizable.strings Binary files differindex 6c9045dc1..6a10a5bae 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/InfoPlist.strings b/iOSClient/Supporting Files/vi.lproj/InfoPlist.strings Binary files differindex b907f6e78..b58e1cf05 100644 --- a/iOSClient/Supporting Files/vi.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/vi.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/vi.lproj/Localizable.strings b/iOSClient/Supporting Files/vi.lproj/Localizable.strings Binary files differindex 308165814..175916657 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/InfoPlist.strings b/iOSClient/Supporting Files/zh-Hans.lproj/InfoPlist.strings Binary files differindex 349e58003..a732957e9 100644 --- a/iOSClient/Supporting Files/zh-Hans.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/zh-Hans.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings Binary files differindex a71f2177c..945bdd621 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 b907f6e78..b58e1cf05 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 859f5f7a9..ab783619a 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 95c7b93b2..946102aad 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 614e4f002..2df137685 100755 --- a/iOSClient/Transfers/NCTransferCell.swift +++ b/iOSClient/Transfers/NCTransferCell.swift @@ -73,6 +73,8 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP override func awakeFromNib() { super.awakeFromNib() + isAccessibilityElement = true + imageItem.layer.cornerRadius = 6 imageItem.layer.masksToBounds = true @@ -120,6 +122,13 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP func setButtonMore(named: String, image: UIImage) { namedButtonMore = named imageMore.image = image + self.accessibilityCustomActions = [ + UIAccessibilityCustomAction( + name: NSLocalizedString("_cancel_", comment: ""), + target: self, + selector: #selector(touchUpInsideMore)) + ] + } } diff --git a/iOSClient/Transfers/NCTransfers.swift b/iOSClient/Transfers/NCTransfers.swift index bc5d022b0..30827eb70 100644 --- a/iOSClient/Transfers/NCTransfers.swift +++ b/iOSClient/Transfers/NCTransfers.swift @@ -252,6 +252,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { cell.labelInfo.text = "" break } + cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "") // Remove last separator if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 { diff --git a/iOSClient/Trash/Cell/NCTrashListCell.swift b/iOSClient/Trash/Cell/NCTrashListCell.swift index 5c953a6b8..28b2fa8b6 100644 --- a/iOSClient/Trash/Cell/NCTrashListCell.swift +++ b/iOSClient/Trash/Cell/NCTrashListCell.swift @@ -50,6 +50,20 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCell { override func awakeFromNib() { super.awakeFromNib() + isAccessibilityElement = true + + self.accessibilityCustomActions = [ + UIAccessibilityCustomAction( + name: NSLocalizedString("_restore_", comment: ""), + target: self, + selector: #selector(touchUpInsideRestore)), + UIAccessibilityCustomAction( + name: NSLocalizedString("_delete_", comment: ""), + target: self, + selector: #selector(touchUpInsideMore)) + + ] + imageRestore.image = NCBrandColor.cacheImages.buttonRestore imageMore.image = NCUtility.shared.loadImage(named: "trash") @@ -119,18 +133,21 @@ protocol NCTrashCell { func selectMode(_ status: Bool) func selected(_ status: Bool) } -extension NCTrashCell { + +extension NCTrashCell 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 tableTrash.directory { self.imageItem.image = NCBrandColor.cacheImages.folder - self.labelInfo?.text = CCUtility.dateDiff(tableTrash.date as Date) + infoText = CCUtility.dateDiff(tableTrash.date as Date) } else { self.imageItem.image = image - self.labelInfo?.text = CCUtility.dateDiff(tableTrash.date as Date) + ", " + CCUtility.transformedSize(tableTrash.size) + infoText = CCUtility.dateDiff(tableTrash.date as Date) + ", " + CCUtility.transformedSize(tableTrash.size) } + self.labelInfo?.text = infoText + self.accessibilityLabel = tableTrash.trashbinFileName + ", " + infoText } } diff --git a/iOSClient/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift index 66c4fde96..8261bfc9b 100644 --- a/iOSClient/Trash/NCTrash+CollectionView.swift +++ b/iOSClient/Trash/NCTrash+CollectionView.swift @@ -62,8 +62,10 @@ extension NCTrash: UICollectionViewDataSource { 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 diff --git a/iOSClient/Utility/CCUtility.h b/iOSClient/Utility/CCUtility.h index e2aee0e86..2d50ba80d 100644 --- a/iOSClient/Utility/CCUtility.h +++ b/iOSClient/Utility/CCUtility.h @@ -180,12 +180,12 @@ + (NSInteger)getCleanUpDay; + (void)setCleanUpDay:(NSInteger)days; -+ (PDFDisplayDirection)getPDFDisplayDirection; -+ (void)setPDFDisplayDirection:(PDFDisplayDirection)direction; - + (BOOL)getPrivacyScreenEnabled; + (void)setPrivacyScreenEnabled:(BOOL)set; ++ (BOOL)getRemovePhotoCameraRoll; ++ (void)setRemovePhotoCameraRoll:(BOOL)set; + // ===== Varius ===== + (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL; @@ -243,7 +243,7 @@ + (NSString *)getMimeType:(NSString *)fileNameView; -+ (void)extractImageVideoFromAssetLocalIdentifierForUpload:(tableMetadata *)metadataForUpload notification:(BOOL)notification completion:(void(^)(tableMetadata *newMetadata, NSString* fileNamePath))completion; ++ (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 ===== diff --git a/iOSClient/Utility/CCUtility.m b/iOSClient/Utility/CCUtility.m index 0013c4b68..edc4b55b7 100644 --- a/iOSClient/Utility/CCUtility.m +++ b/iOSClient/Utility/CCUtility.m @@ -715,23 +715,6 @@ [UICKeyChainStore setString:daysString forKey:@"cleanUpDay" service:NCGlobal.shared.serviceShareKeyChain]; } -+ (PDFDisplayDirection)getPDFDisplayDirection -{ - NSString *direction = [UICKeyChainStore stringForKey:@"PDFDisplayDirection" service:NCGlobal.shared.serviceShareKeyChain]; - - if (direction == nil) { - return kPDFDisplayDirectionVertical; - } else { - return [direction integerValue]; - } -} - -+ (void)setPDFDisplayDirection:(PDFDisplayDirection)direction -{ - NSString *directionString = [@(direction) stringValue]; - [UICKeyChainStore setString:directionString forKey:@"PDFDisplayDirection" service:NCGlobal.shared.serviceShareKeyChain]; -} - + (BOOL)getPrivacyScreenEnabled { NSString *valueString = [UICKeyChainStore stringForKey:@"privacyScreen" service:NCGlobal.shared.serviceShareKeyChain]; @@ -751,6 +734,17 @@ [UICKeyChainStore setString:sSet forKey:@"privacyScreen" service:NCGlobal.shared.serviceShareKeyChain]; } ++ (BOOL)getRemovePhotoCameraRoll +{ + return [[UICKeyChainStore stringForKey:@"removePhotoCameraRoll" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; +} + ++ (void)setRemovePhotoCameraRoll:(BOOL)set +{ + NSString *sSet = (set) ? @"true" : @"false"; + [UICKeyChainStore setString:sSet forKey:@"removePhotoCameraRoll" service:NCGlobal.shared.serviceShareKeyChain]; +} + #pragma -------------------------------------------------------------------------------------------- #pragma mark ===== Various ===== #pragma -------------------------------------------------------------------------------------------- @@ -1350,7 +1344,7 @@ return path; } -+ (void)extractImageVideoFromAssetLocalIdentifierForUpload:(tableMetadata *)metadataForUpload notification:(BOOL)notification completion:(void(^)(tableMetadata *metadata, NSString* fileNamePath))completion ++ (void)extractImageVideoFromAssetLocalIdentifierForUpload:(tableMetadata *)metadataForUpload completion:(void(^)(tableMetadata *metadata, NSString* fileNamePath))completion { if (metadataForUpload == nil) { return completion(nil, nil); @@ -1360,10 +1354,6 @@ PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[metadata.assetLocalIdentifier] options:nil]; if (!result.count) { - if (notification) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(NCGlobal.shared.errorInternalError), @"errorDescription": @"_err_file_not_found_"}]; - } - return completion(nil, nil); } @@ -1385,21 +1375,24 @@ NSLog(@"cacheAsset: %f", progress); if (error) { - if (notification) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Image request iCloud failed [%@]", error.description]}]; - } - + [[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 *extensionAsset = [[[asset valueForKey:@"filename"] pathExtension] uppercaseString]; NSString *fileName = metadata.fileNameView; - if ([extensionAsset isEqualToString:@"HEIC"] && [CCUtility getFormatCompatibility]) { + if (([extensionAsset isEqualToString:@"HEIC"] || [extensionAsset isEqualToString:@"DNG"]) && [CCUtility getFormatCompatibility]) { CIImage *ciImage = [CIImage imageWithData:imageData]; CIContext *context = [CIContext context]; @@ -1444,10 +1437,7 @@ NSLog(@"cacheAsset: %f", progress); if (error) { - if (notification) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Video request iCloud failed [%@]", error.description]}]; - } - + [[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); } }; @@ -1468,19 +1458,12 @@ dispatch_async(dispatch_get_main_queue(), ^{ if (error) { - - if (notification) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Video request iCloud failed [%@]", error.description]}]; - } - + [[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); } }); @@ -1728,6 +1711,7 @@ stringLatitude = [NSString stringWithFormat:@"+%.4f", latitude]; } else { stringLatitude = [NSString stringWithFormat:@"-%.4f", latitude]; + latitude *= -1; } // conversion 4 decimal +E -W @@ -1737,10 +1721,10 @@ stringLongitude = [NSString stringWithFormat:@"+%.4f", longitude]; } else { stringLongitude = [NSString stringWithFormat:@"-%.4f", longitude]; + longitude *= -1; } if (latitude == 0 || longitude == 0) { - stringLatitude = @"0"; stringLongitude = @"0"; } diff --git a/iOSClient/Utility/NCAskAuthorization.swift b/iOSClient/Utility/NCAskAuthorization.swift index 28ce6e902..1d02f9d1e 100644 --- a/iOSClient/Utility/NCAskAuthorization.swift +++ b/iOSClient/Utility/NCAskAuthorization.swift @@ -29,6 +29,8 @@ class NCAskAuthorization: NSObject { return instance }() + private(set) var isRequesting = false + func askAuthorizationAudioRecord(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) { switch AVAudioSession.sharedInstance().recordPermission { @@ -65,7 +67,7 @@ class NCAskAuthorization: NSObject { } } - func askAuthorizationPhotoLibrary(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) { + @objc func askAuthorizationPhotoLibrary(viewController: UIViewController?, completion: @escaping (_ hasPermission: Bool) -> Void) { switch PHPhotoLibrary.authorizationStatus() { case PHAuthorizationStatus.authorized: @@ -85,7 +87,12 @@ class NCAskAuthorization: NSObject { } break case PHAuthorizationStatus.notDetermined: + isRequesting = true PHPhotoLibrary.requestAuthorization { allowed in + self.isRequesting = false + DispatchQueue.main.async { + (UIApplication.shared.delegate as? AppDelegate)?.hidePrivacyProtectionWindow() + } DispatchQueue.main.async { if allowed == PHAuthorizationStatus.authorized { completion(true) @@ -100,33 +107,4 @@ class NCAskAuthorization: NSObject { break } } - - @objc func askAuthorizationLocationManager(completion: @escaping (_ hasFullPermissions: Bool) -> Void) { - - switch CLLocationManager.authorizationStatus() { - case CLAuthorizationStatus.authorizedAlways: - completion(true) - break - /* - case CLAuthorizationStatus.authorizedWhenInUse, CLAuthorizationStatus.denied, CLAuthorizationStatus.restricted: - DispatchQueue.main.async { - NCAutoUpload.shared.startSignificantChangeUpdates() - } - completion(false) - break - case CLAuthorizationStatus.notDetermined: - DispatchQueue.main.async { - NCAutoUpload.shared.startSignificantChangeUpdates() - } - completion(false) - break - */ - default: - DispatchQueue.main.async { - NCAutoUpload.shared.startSignificantChangeUpdates() - } - completion(false) - break - } - } } diff --git a/iOSClient/Utility/NCUserBaseUrl.swift b/iOSClient/Utility/NCUserBaseUrl.swift index c8eee917b..8bb5e6576 100644 --- a/iOSClient/Utility/NCUserBaseUrl.swift +++ b/iOSClient/Utility/NCUserBaseUrl.swift @@ -26,6 +26,7 @@ import Foundation @objc public protocol NCUserBaseUrl { var user: String { get } var urlBase: String { get } + var account: String { get } } public extension NCUserBaseUrl { diff --git a/iOSClient/Utility/NCUtility+Image.swift b/iOSClient/Utility/NCUtility+Image.swift new file mode 100644 index 000000000..a56f96e91 --- /dev/null +++ b/iOSClient/Utility/NCUtility+Image.swift @@ -0,0 +1,91 @@ +// +// NCUtility+Image.swift +// Nextcloud +// +// Created by Henrik Storch on 17.03.22. +// Copyright © 2022 Henrik Storch. All rights reserved. +// +// Author Henrik Storch <henrik.storch@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 SVGKit +import NCCommunication + +extension NCUtility { + func getImageMetadata(_ metadata: tableMetadata, for size: CGFloat) -> UIImage? { + + if let image = getImage(metadata: metadata) { + return image + } + + if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.hasPreview { + NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + } + + if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) { + if let imagePreviewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag) { + return UIImage(contentsOfFile: imagePreviewPath) + } + } + + if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { + return UIImage(named: "noPreviewVideo")?.image(color: .gray, size: size) + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { + return UIImage(named: "noPreviewAudio")?.image(color: .gray, size: size) + } else { + return UIImage(named: "noPreview")?.image(color: .gray, size: size) + } + } + + func getImage(metadata: tableMetadata) -> UIImage? { + let ext = CCUtility.getExtension(metadata.fileNameView) + var image: UIImage? + + if CCUtility.fileProviderStorageExists(metadata) && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue { + + let previewPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)! + let imagePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! + + if ext == "GIF" { + if !FileManager().fileExists(atPath: previewPath) { + NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + } + image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath)) + } else if ext == "SVG" { + if let svgImage = SVGKImage(contentsOfFile: imagePath) { + svgImage.size = CGSize(width: NCGlobal.shared.sizePreview, height: NCGlobal.shared.sizePreview) + if let image = svgImage.uiImage { + if !FileManager().fileExists(atPath: previewPath) { + do { + try image.pngData()?.write(to: URL(fileURLWithPath: previewPath), options: .atomic) + } catch { } + } + return image + } else { + return nil + } + } else { + return nil + } + } else { + NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + image = UIImage(contentsOfFile: imagePath) + } + } + return image + } +} diff --git a/iOSClient/Utility/NCUtility.swift b/iOSClient/Utility/NCUtility.swift index a29f0cdea..191615981 100644 --- a/iOSClient/Utility/NCUtility.swift +++ b/iOSClient/Utility/NCUtility.swift @@ -305,6 +305,19 @@ class NCUtility: NSObject { return true } + @objc func getCustomUserAgentNCText() -> String { + let userAgent: String = CCUtility.getUserAgent() + if UIDevice.current.userInterfaceIdiom == .phone { + // NOTE: Hardcoded (May 2022) + // Tested for iPhone SE (1st), iOS 12; iPhone Pro Max, iOS 15.4 + // 605.1.15 = WebKit build version + // 15E148 = frozen iOS build number according to: https://chromestatus.com/feature/4558585463832576 + return userAgent + " " + "AppleWebKit/605.1.15 Mobile/15E148" + } else { + return userAgent + } + } + @objc func getCustomUserAgentOnlyOffice() -> String { let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")! @@ -353,7 +366,7 @@ class NCUtility: NSObject { var messageUserDefined: String = "" if userStatus?.lowercased() == "online" { - onlineStatus = UIImage(named: "userStatusOnline")!.image(color: UIColor(red: 103.0/255.0, green: 176.0/255.0, blue: 134.0/255.0, alpha: 1.0), size: 50) + onlineStatus = UIImage(named: "circle_fill")!.image(color: UIColor(red: 103.0/255.0, green: 176.0/255.0, blue: 134.0/255.0, alpha: 1.0), size: 50) messageUserDefined = NSLocalizedString("_online_", comment: "") } if userStatus?.lowercased() == "away" { @@ -474,25 +487,27 @@ class NCUtility: NSObject { return "" } - func loadImage(named: String, color: UIColor = NCBrandColor.shared.gray, size: CGFloat = 50, symbolConfiguration: Any? = nil) -> UIImage { + func loadImage(named imageName: String, color: UIColor = NCBrandColor.shared.gray, size: CGFloat = 50, symbolConfiguration: Any? = nil) -> UIImage { var image: UIImage? if #available(iOS 13.0, *) { + // see https://stackoverflow.com/questions/71764255 + let sfSymbolName = imageName.replacingOccurrences(of: "_", with: ".") if let symbolConfiguration = symbolConfiguration { - image = UIImage(systemName: named, withConfiguration: symbolConfiguration as? UIImage.Configuration)?.imageColor(color) + image = UIImage(systemName: sfSymbolName, withConfiguration: symbolConfiguration as? UIImage.Configuration)?.imageColor(color) } else { - image = UIImage(systemName: named)?.imageColor(color) + image = UIImage(systemName: sfSymbolName)?.imageColor(color) } if image == nil { - image = UIImage(named: named)?.image(color: color, size: size) + image = UIImage(named: imageName)?.image(color: color, size: size) } } else { - image = UIImage(named: named)?.image(color: color, size: size) + image = UIImage(named: imageName)?.image(color: color, size: size) } - if image != nil { - return image! + if let image = image { + return image } return UIImage(named: "file")!.image(color: color, size: size) @@ -799,6 +814,81 @@ class NCUtility: NSObject { } } } + + func getEncondingDataType(data: Data) -> String.Encoding? { + if let _ = String(data: data, encoding: .utf8) { + return .utf8 + } + if let _ = String(data: data, encoding: .ascii) { + return .ascii + } + if let _ = String(data: data, encoding: .isoLatin1) { + return .isoLatin1 + } + if let _ = String(data: data, encoding: .isoLatin2) { + return .isoLatin2 + } + if let _ = String(data: data, encoding: .windowsCP1250) { + return .windowsCP1250 + } + if let _ = String(data: data, encoding: .windowsCP1251) { + return .windowsCP1251 + } + if let _ = String(data: data, encoding: .windowsCP1252) { + return .windowsCP1252 + } + if let _ = String(data: data, encoding: .windowsCP1253) { + return .windowsCP1253 + } + if let _ = String(data: data, encoding: .windowsCP1254) { + return .windowsCP1254 + } + if let _ = String(data: data, encoding: .macOSRoman) { + return .macOSRoman + } + if let _ = String(data: data, encoding: .japaneseEUC) { + return .japaneseEUC + } + if let _ = String(data: data, encoding: .nextstep) { + return .nextstep + } + if let _ = String(data: data, encoding: .nonLossyASCII) { + return .nonLossyASCII + } + if let _ = String(data: data, encoding: .shiftJIS) { + return .shiftJIS + } + if let _ = String(data: data, encoding: .symbol) { + return .symbol + } + if let _ = String(data: data, encoding: .unicode) { + return .unicode + } + if let _ = String(data: data, encoding: .utf16) { + return .utf16 + } + if let _ = String(data: data, encoding: .utf16BigEndian) { + return .utf16BigEndian + } + if let _ = String(data: data, encoding: .utf16LittleEndian) { + return .utf16LittleEndian + } + if let _ = String(data: data, encoding: .utf32) { + return .utf32 + } + if let _ = String(data: data, encoding: .utf32BigEndian) { + return .utf32BigEndian + } + if let _ = String(data: data, encoding: .utf32LittleEndian) { + return .utf32LittleEndian + } + return nil + } + + func SYSTEM_VERSION_LESS_THAN(version: String) -> Bool { + return UIDevice.current.systemVersion.compare(version, + options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending + } } // MARK: - diff --git a/iOSClient/Utility/NCUtilityFileSystem.swift b/iOSClient/Utility/NCUtilityFileSystem.swift index e64148a37..94ad7ff48 100644 --- a/iOSClient/Utility/NCUtilityFileSystem.swift +++ b/iOSClient/Utility/NCUtilityFileSystem.swift @@ -32,20 +32,6 @@ class NCUtilityFileSystem: NSObject { let fileManager = FileManager.default - @objc func getFileSize(asset: PHAsset) -> Int64 { - - let resources = PHAssetResource.assetResources(for: asset) - - if let resource = resources.first { - if resource.responds(to: #selector(NSDictionary.fileSize)) { - let unsignedInt64 = resource.value(forKey: "fileSize") as! CLong - return Int64(bitPattern: UInt64(unsignedInt64)) - } - } - - return 0 - } - @objc func getFileSize(filePath: String) -> Int64 { do { diff --git a/iOSClient/Viewer/NCViewer.swift b/iOSClient/Viewer/NCViewer.swift index e34784f38..1e36d2490 100644 --- a/iOSClient/Viewer/NCViewer.swift +++ b/iOSClient/Viewer/NCViewer.swift @@ -152,6 +152,8 @@ class NCViewer: NSObject { if editor == NCGlobal.shared.editorOnlyoffice { customUserAgent = NCUtility.shared.getCustomUserAgentOnlyOffice() + } else { + customUserAgent = NCUtility.shared.getCustomUserAgentNCText() } NCUtility.shared.startActivityIndicator(backgroundView: viewController.view, blurEffect: true) @@ -208,11 +210,8 @@ class NCViewer: NSObject { CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath) - let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), editingMode: false, metadata: metadata) - let navigationController = UINavigationController(rootViewController: viewerQuickLook) - navigationController.modalPresentationStyle = .overFullScreen - - viewController.present(navigationController, animated: true) + let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), isEditingEnabled: false, metadata: metadata) + viewController.present(viewerQuickLook, animated: true) } } diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift index 067bf0946..27ca0708e 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift @@ -31,17 +31,17 @@ class NCKTVHTTPCache: NSObject { return instance }() - func getVideoURL(metadata: tableMetadata) -> URL? { + func getVideoURL(metadata: tableMetadata) -> (url: URL?, isProxy: Bool) { if CCUtility.fileProviderStorageExists(metadata) { - return URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) + return (URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)), false) } else { - guard let stringURL = (metadata.serverUrl + "/" + metadata.fileName).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return nil } + guard let stringURL = (metadata.serverUrl + "/" + metadata.fileName).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, false) } - return NCKTVHTTPCache.shared.getProxyURL(stringURL: stringURL) + return (NCKTVHTTPCache.shared.getProxyURL(stringURL: stringURL), true) } } @@ -92,6 +92,11 @@ class NCKTVHTTPCache: NSObject { KTVHTTPCache.cacheDelete(with: videoURL) } + @objc func deleteAllCache() { + + KTVHTTPCache.cacheDeleteAllCaches() + } + func saveCache(metadata: tableMetadata) { if !CCUtility.fileProviderStorageExists(metadata) { @@ -109,6 +114,13 @@ class NCKTVHTTPCache: NSObject { } } + func getDownloadStatusCode(metadata: tableMetadata) -> Int { + + guard let stringURL = (metadata.serverUrl + "/" + metadata.fileName).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return 0 } + let url = URL(string: stringURL) + return KTVHTTPCache.downloadStatusCode(url) + } + private func setupHTTPCache() { KTVHTTPCache.cacheSetMaxCacheLength(NCGlobal.shared.maxHTTPCache) diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift index 9c796b421..60032e9ec 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift @@ -26,29 +26,42 @@ import NCCommunication import UIKit import AVFoundation import MediaPlayer +import JGProgressHUD +import Alamofire class NCPlayer: NSObject { - + internal let appDelegate = UIApplication.shared.delegate as! AppDelegate internal var url: URL - internal var playerToolBar: NCPlayerToolBar? - internal var viewController: UIViewController - - private var imageVideoContainer: imageVideoContainerView - private var detailView: NCViewerMediaDetailView? + internal weak var playerToolBar: NCPlayerToolBar? + internal weak var viewController: UIViewController? + internal var autoPlay: Bool + internal var isProxy: Bool + internal var isStartPlayer: Bool + internal var isStartObserver: Bool + internal var subtitleUrls: [URL] = [] + internal var currentSubtitle: URL? + + private weak var imageVideoContainer: imageVideoContainerView? + private weak var detailView: NCViewerMediaDetailView? private var observerAVPlayerItemDidPlayToEndTime: Any? private var observerAVPlayertTime: Any? - public var player: AVPlayer? - public var durationTime: CMTime = .zero - public var metadata: tableMetadata - public var videoLayer: AVPlayerLayer? + var kvoPlayerObserver: NSKeyValueObservation? + var player: AVPlayer? + var durationTime: CMTime = .zero + var metadata: tableMetadata + var videoLayer: AVPlayerLayer? // MARK: - View Life Cycle - init(url: URL, autoPlay: Bool, imageVideoContainer: imageVideoContainerView, playerToolBar: NCPlayerToolBar?, metadata: tableMetadata, detailView: NCViewerMediaDetailView?, viewController: UIViewController) { - + init(url: URL, autoPlay: Bool, isProxy: Bool, imageVideoContainer: imageVideoContainerView, playerToolBar: NCPlayerToolBar?, metadata: tableMetadata, detailView: NCViewerMediaDetailView?, viewController: UIViewController) { + self.url = url + self.autoPlay = autoPlay + self.isProxy = isProxy + self.isStartPlayer = false + self.isStartObserver = false self.imageVideoContainer = imageVideoContainer self.playerToolBar = playerToolBar self.metadata = metadata @@ -56,7 +69,7 @@ class NCPlayer: NSObject { self.viewController = viewController super.init() - + do { try AVAudioSession.sharedInstance().setCategory(.playback) try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.none) @@ -64,51 +77,48 @@ class NCPlayer: NSObject { } catch { print(error) } - - #if MFFFLIB + } + + deinit { + + print("deinit NCPlayer with ocId \(metadata.ocId)") + deactivateObserver() + } + + func openAVPlayer() { + +#if MFFFLIB + MFFF.shared.setDelegate = self + MFFF.shared.dismissMessage() + NotificationCenter.default.addObserver(self, selector: #selector(convertVideoDidFinish(_:)), name: NSNotification.Name(rawValue: self.metadata.ocId), object: nil) + if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: NCGlobal.shared.fileNameVideoEncoded) { self.url = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: NCGlobal.shared.fileNameVideoEncoded)) + self.isProxy = false } if MFFF.shared.existsMFFFSession(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))) { return - } else { - MFFF.shared.dismissMessage() } - #endif - - openAVPlayer() { status, error in - - switch status { - case .loaded: - if autoPlay { - self.player?.play() - } - break - case .failed: - #if MFFFLIB - self.convertVideo(error: error) - #else - if let title = error?.localizedDescription, let description = error?.localizedFailureReason { - NCContentPresenter.shared.messageNotification(title, description: description, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max) - } else { - NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max) - } - #endif - break - case .cancelled: - break - default: - break +#endif + + // Check already started + if isStartPlayer { + if !isStartObserver { + print("Play already started - URL: \(self.url)") + activateObserver() + playerToolBar?.show() } + return } - } - - internal func openAVPlayer(completion: @escaping (_ status: AVKeyValueStatus, _ error: NSError?)->()) { - + print("Play URL: \(self.url)") player = AVPlayer(url: self.url) + playerToolBar?.show() playerToolBar?.setMetadata(self.metadata) - +#if MFFFLIB + setUpForSubtitle() +#endif + if metadata.livePhoto { player?.isMuted = false } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { @@ -120,70 +130,133 @@ class NCPlayer: NSObject { } } + let observerAVPlayertStatus = self.player?.currentItem?.observe(\.status, options: [.new,.initial]) { player, change in + + if let player = self.player, + let playerItem = player.currentItem, + let object = player.currentItem, + playerItem === object { + + if self.isStartPlayer { + return + } + if (playerItem.status == .readyToPlay || playerItem.status == .failed) { + print("Player ready") + self.startPlayer() + } else { + print("Player not ready") + } + } + } + + if let observerAVPlayertStatus = observerAVPlayertStatus{ + kvoPlayerObserver = observerAVPlayertStatus + } + } + + func startPlayer() { + player?.currentItem?.asset.loadValuesAsynchronously(forKeys: ["playable"], completionHandler: { - + var error: NSError? = nil let status = self.player?.currentItem?.asset.statusOfValue(forKey: "playable", error: &error) ?? .unknown - + DispatchQueue.main.async { - if status == .loaded { + + switch status { + case .loaded: + self.durationTime = self.player?.currentItem?.asset.duration ?? .zero NCManageDatabase.shared.addVideoTime(metadata: self.metadata, time: nil, durationTime: self.durationTime) - self.activateObserver(playerToolBar: self.playerToolBar) - self.videoLayer = AVPlayerLayer(player: self.player) - self.videoLayer!.frame = self.imageVideoContainer.bounds + self.videoLayer!.frame = self.imageVideoContainer?.bounds ?? .zero self.videoLayer!.videoGravity = .resizeAspect - + if self.metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { - - self.imageVideoContainer.layer.addSublayer(self.videoLayer!) - self.imageVideoContainer.playerLayer = self.videoLayer - self.imageVideoContainer.metadata = self.metadata - self.imageVideoContainer.image = self.imageVideoContainer.image?.image(alpha: 0) + self.imageVideoContainer?.layer.addSublayer(self.videoLayer!) + self.imageVideoContainer?.playerLayer = self.videoLayer + self.imageVideoContainer?.metadata = self.metadata + self.imageVideoContainer?.image = self.imageVideoContainer?.image?.image(alpha: 0) } - + self.playerToolBar?.setBarPlayer(ncplayer: self) self.generatorImagePreview() if !(self.detailView?.isShow() ?? false) { NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":self.metadata.ocId, "enableTimerAutoHide": false]) } + self.activateObserver() + if self.autoPlay { + self.player?.play() + } + self.isStartPlayer = true + break + + case .failed: + + self.playerToolBar?.hide() + if self.isProxy && NCKTVHTTPCache.shared.getDownloadStatusCode(metadata: self.metadata) == 200 { + let alertController = UIAlertController(title: NSLocalizedString("_error_", value: "Error", comment: ""), message: NSLocalizedString("_video_not_streamed_", comment: ""), preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", value: "Yes", comment: ""), style: .default, handler: { _ in + self.downloadVideo() + })) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", value: "No", comment: ""), style: .default, handler: { _ in })) + self.viewController?.present(alertController, animated: true) + } else { +#if MFFFLIB + if error?.code == AVError.Code.fileFormatNotRecognized.rawValue { + self.convertVideo(withAlert: true) + break + } +#endif + if let title = error?.localizedDescription, let description = error?.localizedFailureReason { + NCContentPresenter.shared.messageNotification(title, description: description, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max) + } else { + NCContentPresenter.shared.messageNotification("_error_", description: "_error_something_wrong_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorGeneric, priority: .max) + } + } + break + + case .cancelled: + break + + default: + break } - - completion(status, error) } }) } - deinit { - print("deinit NCPlayer") - - deactivateObserver() - } + func activateObserver() { + print("activating Observer ocId \(metadata.ocId)") - func activateObserver(playerToolBar: NCPlayerToolBar?) { + observerAVPlayerItemDidPlayToEndTime = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { [weak self] notification in - self.playerToolBar = playerToolBar + guard let self = self else { + return + } - // At end go back to start & show toolbar - observerAVPlayerItemDidPlayToEndTime = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main) { notification in if let item = notification.object as? AVPlayerItem, let currentItem = self.player?.currentItem, item == currentItem { - + NCKTVHTTPCache.shared.saveCache(metadata: self.metadata) - + self.videoSeek(time: .zero) - + if !(self.detailView?.isShow() ?? false) { NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId": self.metadata.ocId, "enableTimerAutoHide": false]) } - + self.playerToolBar?.updateToolBar() } } // Evey 1 second update toolbar - observerAVPlayertTime = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: .main, using: { _ in + observerAVPlayertTime = player?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: .main, using: { [weak self] _ in + + guard let self = self else { + return + } + if self.player?.currentItem?.status == .readyToPlay { self.playerToolBar?.updateToolBar() } @@ -192,41 +265,55 @@ class NCPlayer: NSObject { NotificationCenter.default.addObserver(self, selector: #selector(generatorImagePreview), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationWillResignActive), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(playerPause), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterPauseMedia), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(playerPlay), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterPlayMedia), object: nil) + + if let player = self.player { + NotificationCenter.default.addObserver(self, selector: #selector(playerStalled), name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: player.currentItem) + } + + isStartObserver = true } func deactivateObserver() { + print("deactivating Observer ocId \(metadata.ocId)") + if isPlay() { playerPause() } - - self.playerToolBar = nil + + self.kvoPlayerObserver?.invalidate() + self.kvoPlayerObserver = nil if let observerAVPlayerItemDidPlayToEndTime = self.observerAVPlayerItemDidPlayToEndTime { NotificationCenter.default.removeObserver(observerAVPlayerItemDidPlayToEndTime) } - self.observerAVPlayerItemDidPlayToEndTime = nil + observerAVPlayerItemDidPlayToEndTime = nil - if let observerAVPlayertTime = self.observerAVPlayertTime { - player?.removeTimeObserver(observerAVPlayertTime) + if let observerAVPlayertTime = self.observerAVPlayertTime, + let player = player { + player.removeTimeObserver(observerAVPlayertTime) } - self.observerAVPlayertTime = nil + observerAVPlayertTime = nil NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationWillResignActive), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil) - + + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterPauseMedia), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterPlayMedia), object: nil) + + isStartObserver = false } // MARK: - NotificationCenter @objc func applicationDidEnterBackground(_ notification: NSNotification) { - + if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue, let playerToolBar = self.playerToolBar { if !playerToolBar.isPictureInPictureActive() { playerPause() @@ -238,24 +325,30 @@ class NCPlayer: NSObject { playerToolBar?.updateToolBar() } - + // MARK: - func isPlay() -> Bool { if player?.rate == 1 { return true } else { return false } } - + + @objc func playerStalled() { + + print("current player \(String(describing: player)) stalled.\nCalling playerPlay()") + playerPlay() + } + @objc func playerPlay() { - + player?.play() - self.playerToolBar?.updateToolBar() + playerToolBar?.updateToolBar() } - + @objc func playerPause() { - + player?.pause() - self.playerToolBar?.updateToolBar() + playerToolBar?.updateToolBar() if let playerToolBar = self.playerToolBar, playerToolBar.isPictureInPictureActive() { playerToolBar.pictureInPictureController?.stopPictureInPicture() @@ -265,7 +358,7 @@ class NCPlayer: NSObject { func videoSeek(time: CMTime) { player?.seek(to: time) - self.saveTime(time) + saveTime(time) } func saveTime(_ time: CMTime) { @@ -314,9 +407,54 @@ class NCPlayer: NSObject { try data.write(to: URL(fileURLWithPath: fileNameIconLocalPath), options: .atomic) } } catch let error as NSError { + print("GeneratorImagePreview localized error:") print(error.localizedDescription) } } } -} + internal func downloadVideo(requiredConvert: Bool = false) { + + guard let view = appDelegate.window?.rootViewController?.view else { return } + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName + let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! + let hud = JGProgressHUD() + var downloadRequest: DownloadRequest? + + hud.indicatorView = JGProgressHUDRingIndicatorView() + if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView { + indicatorView.ringWidth = 1.5 + } + hud.show(in: view) + hud.textLabel.text = NSLocalizedString(metadata.fileNameView, comment: "") + hud.detailTextLabel.text = NSLocalizedString("_tap_to_cancel_", comment: "") + hud.tapOnHUDViewBlock = { hud in + downloadRequest?.cancel() + } + + NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath) { request in + downloadRequest = request + } taskHandler: { task in + // task + } progressHandler: { progress in + hud.progress = Float(progress.fractionCompleted) + } completionHandler: { _, _, _, _, _, error, _, _ in + if error == nil { + NCManageDatabase.shared.addLocalFile(metadata: self.metadata) + let urlVideo = NCKTVHTTPCache.shared.getVideoURL(metadata: self.metadata) + if let url = urlVideo.url { + self.url = url + self.isProxy = urlVideo.isProxy + if requiredConvert { +#if MFFFLIB + self.convertVideo(withAlert: false) +#endif + } else { + self.openAVPlayer() + } + } + } + hud.dismiss() + } + } +} diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift index b28862d4b..297b111a1 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift @@ -30,8 +30,10 @@ import MediaPlayer class NCPlayerToolBar: UIView { - @IBOutlet weak var playerTopToolBarView: UIView! + @IBOutlet weak var playerTopToolBarView: UIStackView! + @IBOutlet weak var playerToolBarView: UIView! @IBOutlet weak var pipButton: UIButton! + @IBOutlet weak var subtitleButton: UIButton! @IBOutlet weak var muteButton: UIButton! @IBOutlet weak var playButton: UIButton! @IBOutlet weak var forwardButton: UIButton! @@ -39,19 +41,32 @@ class NCPlayerToolBar: UIView { @IBOutlet weak var playbackSlider: UISlider! @IBOutlet weak var labelLeftTime: UILabel! @IBOutlet weak var labelCurrentTime: UILabel! - + enum sliderEventType { case began case ended case moved } - private var ncplayer: NCPlayer? + var ncplayer: NCPlayer? private var metadata: tableMetadata? private var wasInPlay: Bool = false private var playbackSliderEvent: sliderEventType = .ended private var timerAutoHide: Timer? + private var timerAutoHideSeconds: Double { + get { + if NCUtility.shared.isSimulator() { // for test + return 15 + } else { + return 3.5 + } + } + } + + +// NCUtility.shared.isSimulatorOrTestFlight() + var pictureInPictureController: AVPictureInPictureController? weak var viewerMediaPage: NCViewerMediaPage? @@ -60,27 +75,21 @@ class NCPlayerToolBar: UIView { override func awakeFromNib() { super.awakeFromNib() - // for disable gesture of UIPageViewController - let panRecognizer = UIPanGestureRecognizer(target: self, action: nil) - addGestureRecognizer(panRecognizer) - let singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didSingleTapWith(gestureRecognizer:))) - addGestureRecognizer(singleTapGestureRecognizer) - - self.layer.cornerRadius = 15 - self.layer.masksToBounds = true - let blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) blurEffectView.frame = self.bounds blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - self.insertSubview(blurEffectView, at: 0) - + playerToolBarView.insertSubview(blurEffectView, at: 0) playerTopToolBarView.layer.cornerRadius = 10 playerTopToolBarView.layer.masksToBounds = true + playerTopToolBarView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapTopToolBarWith(gestureRecognizer:)))) let blurEffectTopToolBarView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) blurEffectTopToolBarView.frame = playerTopToolBarView.bounds blurEffectTopToolBarView.autoresizingMask = [.flexibleWidth, .flexibleHeight] playerTopToolBarView.insertSubview(blurEffectTopToolBarView, at: 0) + playerToolBarView.layer.cornerRadius = 10 + playerToolBarView.layer.masksToBounds = true + playerToolBarView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapToolBarWith(gestureRecognizer:)))) pipButton.setImage(NCUtility.shared.loadImage(named: "pip.enter", color: .lightGray), for: .normal) pipButton.isEnabled = false @@ -88,6 +97,10 @@ class NCPlayerToolBar: UIView { muteButton.setImage(NCUtility.shared.loadImage(named: "audioOff", color: .lightGray), for: .normal) muteButton.isEnabled = false + subtitleButton.setImage(NCUtility.shared.loadImage(named: "captions.bubble", color: .white), for: .normal) + subtitleButton.isEnabled = true + subtitleButton.isHidden = true + playbackSlider.value = 0 playbackSlider.minimumValue = 0 playbackSlider.maximumValue = 0 @@ -100,9 +113,17 @@ class NCPlayerToolBar: UIView { labelLeftTime.text = NCUtility.shared.stringFromTime(.zero) labelLeftTime.textColor = .lightGray + backButton.setImage(NCUtility.shared.loadImage(named: "gobackward.10", color: .lightGray), for: .normal) backButton.isEnabled = false - playButton.setImage(NCUtility.shared.loadImage(named: "play.fill", color: .lightGray), for: .normal) + + if #available(iOS 13.0, *) { + playButton.setImage(NCUtility.shared.loadImage(named: "play.fill", color: .lightGray, symbolConfiguration: UIImage.SymbolConfiguration(pointSize: 30)), for: .normal) + } else { + playButton.setImage(NCUtility.shared.loadImage(named: "play.fill", color: .lightGray, size: 30), for: .normal) + } playButton.isEnabled = false + + forwardButton.setImage(NCUtility.shared.loadImage(named: "goforward.10", color: .lightGray), for: .normal) forwardButton.isEnabled = false NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: AVAudioSession.interruptionNotification, object: nil) @@ -121,12 +142,12 @@ class NCPlayerToolBar: UIView { } // MARK: - - + func setMetadata(_ metadata: tableMetadata) { - + self.metadata = metadata } - + func setBarPlayer(ncplayer: NCPlayer) { self.ncplayer = ncplayer @@ -143,7 +164,7 @@ class NCPlayerToolBar: UIView { } public func updateToolBar() { - + guard let ncplayer = self.ncplayer else { return } // MUTE @@ -209,7 +230,7 @@ class NCPlayerToolBar: UIView { // MARK: Handle Notifications @objc func handleRouteChange(notification: Notification) { - + guard let userInfo = notification.userInfo, let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt, let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { return } switch reason { @@ -265,25 +286,23 @@ class NCPlayerToolBar: UIView { guard let metadata = self.metadata, ncplayer != nil, !metadata.livePhoto else { return } if metadata.classFile != NCCommunicationCommon.typeClassFile.video.rawValue && metadata.classFile != NCCommunicationCommon.typeClassFile.audio.rawValue { return } - #if MFFFLIB +#if MFFFLIB if MFFF.shared.existsMFFFSession(url: URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))) { self.hide() return } - #endif - +#endif + timerAutoHide?.invalidate() if enableTimerAutoHide { startTimerAutoHide() } if !self.isHidden { return } - + UIView.animate(withDuration: 0.3, animations: { self.alpha = 1 - self.playerTopToolBarView.alpha = 1 }, completion: { (_: Bool) in self.isHidden = false - self.playerTopToolBarView.isHidden = false }) updateToolBar() @@ -298,10 +317,8 @@ class NCPlayerToolBar: UIView { UIView.animate(withDuration: 0.3, animations: { self.alpha = 0 - self.playerTopToolBarView.alpha = 0 }, completion: { (_: Bool) in self.isHidden = true - self.playerTopToolBarView.isHidden = true }) } @@ -315,7 +332,7 @@ class NCPlayerToolBar: UIView { private func startTimerAutoHide() { timerAutoHide?.invalidate() - timerAutoHide = Timer.scheduledTimer(timeInterval: 3.5, target: self, selector: #selector(automaticHide), userInfo: nil, repeats: false) + timerAutoHide = Timer.scheduledTimer(timeInterval: timerAutoHideSeconds, target: self, selector: #selector(automaticHide), userInfo: nil, repeats: false) } private func reStartTimerAutoHide() { @@ -369,7 +386,7 @@ class NCPlayerToolBar: UIView { timerAutoHide?.invalidate() } - + // MARK: - Event / Gesture @objc func onSliderValChanged(slider: UISlider, event: UIEvent) { @@ -403,19 +420,11 @@ class NCPlayerToolBar: UIView { // MARK: - Action - @objc func didSingleTapWith(gestureRecognizer: UITapGestureRecognizer) { - // nothing - } + @objc func tapTopToolBarWith(gestureRecognizer: UITapGestureRecognizer) { } - @IBAction func buttonPlayerToolBarTouchInside(_ sender: UIButton) { - // nothing - } - - @IBAction func buttonPlayerTopToolBarTouchInside(_ sender: UIButton) { - // nothing - } + @objc func tapToolBarWith(gestureRecognizer: UITapGestureRecognizer) { } - @IBAction func playerPause(_ sender: Any) { + @IBAction func tapPlayerPause(_ sender: Any) { if ncplayer?.player?.timeControlStatus == .playing { ncplayer?.playerPause() @@ -429,6 +438,7 @@ class NCPlayerToolBar: UIView { if let reason = ncplayer?.player?.reasonForWaitingToPlay { switch reason { case .evaluatingBufferingRate: + self.ncplayer?.player?.playImmediately(atRate: 1) print("reasonForWaitingToPlay.evaluatingBufferingRate") case .toMinimizeStalls: print("reasonForWaitingToPlay.toMinimizeStalls") @@ -441,7 +451,7 @@ class NCPlayerToolBar: UIView { } } - @IBAction func setMute(_ sender: Any) { + @IBAction func tapMute(_ sender: Any) { let mute = CCUtility.getAudioMute() @@ -451,7 +461,7 @@ class NCPlayerToolBar: UIView { reStartTimerAutoHide() } - @IBAction func setPip(_ sender: Any) { + @IBAction func tapPip(_ sender: Any) { guard let videoLayer = ncplayer?.videoLayer else { return } @@ -472,32 +482,36 @@ class NCPlayerToolBar: UIView { } } - @IBAction func forwardButtonSec(_ sender: Any) { + @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() - } - */ + if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { + skip(seconds: 10) + } else if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { + forward() + } + */ } - @IBAction func backButtonSec(_ sender: Any) { + @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() - } - */ + if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { + skip(seconds: -10) + } else if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { + backward() + } + */ + } + + @IBAction func tapSubtitle(_ sender: Any) { + self.ncplayer?.showAlertSubtitles() } - + func forward() { var index: Int = 0 diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.xib b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.xib new file mode 100644 index 000000000..9007a31d4 --- /dev/null +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.xib @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="retina6_1" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> + <capability name="Image references" minToolsVersion="12.0"/> + <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"/> + <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="NCPlayerToolBar" customModule="Nextcloud" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="414" height="896"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="XfW-XC-eMf" userLabel="Player Top Tool Bar"> + <rect key="frame" x="299" y="54" width="105" height="35"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a3d-ja-utA"> + <rect key="frame" x="5" y="5" width="25" height="25"/> + <constraints> + <constraint firstAttribute="height" constant="25" id="TIW-zu-jSX"/> + <constraint firstAttribute="width" constant="25" id="jR3-bv-VnZ"/> + </constraints> + <state key="normal" image="pip.enter" catalog="system"/> + <connections> + <action selector="tapPip:" destination="iN0-l3-epB" eventType="touchUpInside" id="fB5-g1-OnB"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mqe-Hd-USv"> + <rect key="frame" x="40" y="5" width="25" height="25"/> + <constraints> + <constraint firstAttribute="width" constant="25" id="HQs-6R-hb9"/> + <constraint firstAttribute="height" constant="25" id="s43-Qc-Shn"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" image="captions.bubble" catalog="system"/> + <connections> + <action selector="tapSubtitle:" destination="iN0-l3-epB" eventType="touchUpInside" id="Cxi-6E-bKD"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fml-c2-FMY"> + <rect key="frame" x="75" y="5" width="25" height="25"/> + <constraints> + <constraint firstAttribute="width" constant="25" id="S8g-UR-4zh"/> + <constraint firstAttribute="height" constant="25" id="zjo-O1-SI2"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" image="audioOn"/> + <connections> + <action selector="tapMute:" destination="iN0-l3-epB" eventType="touchUpInside" id="FWr-cn-Pgw"/> + </connections> + </button> + </subviews> + <directionalEdgeInsets key="directionalLayoutMargins" top="5" leading="5" bottom="5" trailing="5"/> + </stackView> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="85m-50-8yp"> + <rect key="frame" x="10" y="794" width="394" height="58"/> + <subviews> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ixi-yR-HDH" userLabel="Container play"> + <rect key="frame" x="0.0" y="0.0" width="118" height="58"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uP7-aY-x4n"> + <rect key="frame" x="3.5" y="9" width="40" height="40"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="0jv-Tl-Nch"/> + <constraint firstAttribute="height" constant="40" id="jnZ-cX-iJY"/> + </constraints> + <state key="normal" image="gobackward.10" catalog="system"/> + <connections> + <action selector="tapBack:" destination="iN0-l3-epB" eventType="touchUpInside" id="q3g-BH-iUc"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hx9-d5-yiD"> + <rect key="frame" x="39" y="9" width="40" height="40"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="Ime-ag-2Hm"/> + <constraint firstAttribute="height" constant="40" id="snM-fJ-0LV"/> + </constraints> + <state key="normal"> + <imageReference key="image" image="play.fill" catalog="system" symbolScale="default"/> + <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/> + </state> + <connections> + <action selector="tapPlayerPause:" destination="iN0-l3-epB" eventType="touchUpInside" id="XHf-om-3g9"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bGn-IC-3V1"> + <rect key="frame" x="74.5" y="9" width="40" height="40"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="VDT-no-B6f"/> + <constraint firstAttribute="width" constant="40" id="eA5-wy-Sqb"/> + </constraints> + <state key="normal" image="goforward.10" catalog="system"/> + <connections> + <action selector="tapForward:" destination="iN0-l3-epB" eventType="touchUpInside" id="45z-IH-Fyr"/> + </connections> + </button> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="hx9-d5-yiD" firstAttribute="centerY" secondItem="ixi-yR-HDH" secondAttribute="centerY" id="Brn-9w-mrJ"/> + <constraint firstItem="bGn-IC-3V1" firstAttribute="centerY" secondItem="ixi-yR-HDH" secondAttribute="centerY" id="JBD-51-iYm"/> + <constraint firstItem="uP7-aY-x4n" firstAttribute="centerY" secondItem="ixi-yR-HDH" secondAttribute="centerY" id="MPU-Hg-zQa"/> + <constraint firstItem="hx9-d5-yiD" firstAttribute="centerX" secondItem="ixi-yR-HDH" secondAttribute="centerX" id="Vyv-jg-Cwu"/> + <constraint firstItem="uP7-aY-x4n" firstAttribute="centerX" secondItem="ixi-yR-HDH" secondAttribute="centerX" multiplier="0.4" id="bwk-Bx-qVb"/> + <constraint firstItem="bGn-IC-3V1" firstAttribute="centerX" secondItem="ixi-yR-HDH" secondAttribute="centerX" multiplier="1.6" id="wJP-ph-5c5"/> + </constraints> + </view> + <slider opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="MY0-FC-j88"> + <rect key="frame" x="121" y="14" width="265" height="31"/> + </slider> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="99:99:99" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="svM-TQ-AyQ"> + <rect key="frame" x="338.5" y="44" width="45.5" height="12"/> + <fontDescription key="fontDescription" type="system" pointSize="10"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OHB-2J-Gqb"> + <rect key="frame" x="123" y="44" width="45" height="12"/> + <fontDescription key="fontDescription" type="system" pointSize="10"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="ixi-yR-HDH" firstAttribute="centerY" secondItem="85m-50-8yp" secondAttribute="centerY" id="5sz-fh-GbG"/> + <constraint firstItem="OHB-2J-Gqb" firstAttribute="leading" secondItem="MY0-FC-j88" secondAttribute="leading" id="6n7-If-xdS"/> + <constraint firstItem="ixi-yR-HDH" firstAttribute="height" secondItem="85m-50-8yp" secondAttribute="height" id="8xM-7l-FXN"/> + <constraint firstAttribute="height" constant="58" id="9Uv-dV-to4"/> + <constraint firstItem="svM-TQ-AyQ" firstAttribute="top" secondItem="MY0-FC-j88" secondAttribute="bottom" id="Aan-ac-nr9"/> + <constraint firstItem="MY0-FC-j88" firstAttribute="leading" secondItem="ixi-yR-HDH" secondAttribute="trailing" constant="5" id="BAz-4C-w3A"/> + <constraint firstAttribute="trailing" secondItem="svM-TQ-AyQ" secondAttribute="trailing" constant="10" id="JXl-DO-x6b"/> + <constraint firstItem="ixi-yR-HDH" firstAttribute="leading" secondItem="85m-50-8yp" secondAttribute="leading" id="Kh2-nU-bMU"/> + <constraint firstItem="ixi-yR-HDH" firstAttribute="width" secondItem="85m-50-8yp" secondAttribute="width" multiplier="0.3" id="gXG-4n-Ee3"/> + <constraint firstAttribute="trailing" secondItem="MY0-FC-j88" secondAttribute="trailing" constant="10" id="hBO-J2-Kdo"/> + <constraint firstItem="OHB-2J-Gqb" firstAttribute="top" secondItem="MY0-FC-j88" secondAttribute="bottom" id="jdb-Vq-WoF"/> + <constraint firstItem="MY0-FC-j88" firstAttribute="centerY" secondItem="85m-50-8yp" secondAttribute="centerY" id="lG8-DN-rTE"/> + </constraints> + </view> + </subviews> + <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="85m-50-8yp" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="10" id="5H2-Gg-PEb"/> + <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="85m-50-8yp" secondAttribute="trailing" constant="10" id="BXT-Qo-qFl"/> + <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="85m-50-8yp" secondAttribute="bottom" constant="10" id="N7Q-PF-7lb"/> + <constraint firstItem="XfW-XC-eMf" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="10" id="fq3-6h-kkx"/> + <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="XfW-XC-eMf" secondAttribute="trailing" constant="10" id="uYG-Ai-CGv"/> + </constraints> + <connections> + <outlet property="backButton" destination="uP7-aY-x4n" id="SyC-qV-IMq"/> + <outlet property="forwardButton" destination="bGn-IC-3V1" id="0OZ-f2-eWU"/> + <outlet property="labelCurrentTime" destination="OHB-2J-Gqb" id="pFy-CJ-x2A"/> + <outlet property="labelLeftTime" destination="svM-TQ-AyQ" id="UDV-Lh-12z"/> + <outlet property="muteButton" destination="Fml-c2-FMY" id="Fo1-Ep-ZPz"/> + <outlet property="pipButton" destination="a3d-ja-utA" id="9nt-RD-IXd"/> + <outlet property="playButton" destination="hx9-d5-yiD" id="Enk-Ge-2Yx"/> + <outlet property="playbackSlider" destination="MY0-FC-j88" id="bVe-Kc-80k"/> + <outlet property="playerToolBarView" destination="85m-50-8yp" id="eZK-p1-v65"/> + <outlet property="playerTopToolBarView" destination="XfW-XC-eMf" id="Qdp-IW-YhT"/> + <outlet property="subtitleButton" destination="Mqe-Hd-USv" id="ZHN-bf-WT5"/> + </connections> + <point key="canvasLocation" x="137.68115942028987" y="152.67857142857142"/> + </view> + </objects> + <resources> + <image name="audioOn" width="28" height="28"/> + <image name="captions.bubble" catalog="system" width="128" height="110"/> + <image name="gobackward.10" catalog="system" width="121" height="128"/> + <image name="goforward.10" catalog="system" width="121" height="128"/> + <image name="pip.enter" catalog="system" width="128" height="96"/> + <image name="play.fill" catalog="system" width="116" height="128"/> + </resources> +</document> diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitlePlayer.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitlePlayer.swift new file mode 100644 index 000000000..3955fe8b5 --- /dev/null +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitlePlayer.swift @@ -0,0 +1,405 @@ +// +// NCSubtitlePlayer.swift +// Nextcloud +// +// Created by Federico Malagoni on 18/02/22. +// Copyright © 2022 Federico Malagoni. All rights reserved. +// Copyright © 2022 Marino Faggiana All rights reserved. +// +// Author Federico Malagoni <federico.malagoni@astrairidium.com> +// 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 AVKit + +extension NCPlayer { + + private struct AssociatedKeys { + static var FontKey = "FontKey" + static var ColorKey = "FontKey" + static var SubtitleKey = "SubtitleKey" + static var SubtitleContainerViewKey = "SubtitleContainerViewKey" + static var SubtitleContainerViewHeightKey = "SubtitleContainerViewHeightKey" + static var SubtitleHeightKey = "SubtitleHeightKey" + static var SubtitleWidthKey = "SubtitleWidthKey" + static var SubtitleContainerViewWidthKey = "SubtitleContainerViewWidthKey" + static var SubtitleBottomKey = "SubtitleBottomKey" + static var PayloadKey = "PayloadKey" + } + + private var widthProportion: CGFloat { + return 0.9 + } + + private var bottomConstantPortrait: CGFloat { + get { + if UIDevice.current.hasNotch { + return -60 + } else { + return -40 + } + } set { + _ = newValue + } + } + + private var bottomConstantLandscape: CGFloat { + get { + if UIDevice.current.hasNotch { + return -120 + } else { + return -100 + } + } set { + _ = newValue + } + } + + var subtitleContainerView: UIView? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewKey) as? UIView } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)} + } + + var subtitleLabel: UILabel? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleKey) as? UILabel } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + fileprivate var subtitleLabelHeightConstraint: NSLayoutConstraint? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleHeightKey) as? NSLayoutConstraint } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleHeightKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + fileprivate var subtitleContainerViewHeightConstraint: NSLayoutConstraint? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewHeightKey) as? NSLayoutConstraint } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewHeightKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + fileprivate var subtitleLabelBottomConstraint: NSLayoutConstraint? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleBottomKey) as? NSLayoutConstraint } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleBottomKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + fileprivate var subtitleLabelWidthConstraint: NSLayoutConstraint? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleWidthKey) as? NSLayoutConstraint } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleWidthKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + fileprivate var subtitleContainerViewWidthConstraint: NSLayoutConstraint? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewWidthKey) as? NSLayoutConstraint } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.SubtitleContainerViewWidthKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + fileprivate var parsedPayload: NSDictionary? { + get { return objc_getAssociatedObject(self, &AssociatedKeys.PayloadKey) as? NSDictionary } + set (value) { objc_setAssociatedObject(self, &AssociatedKeys.PayloadKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + func setUpForSubtitle() { + self.subtitleUrls.removeAll() + if let url = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId) { + let enumerator = FileManager.default.enumerator(atPath: url) + let filePaths = (enumerator?.allObjects as? [String]) + if let filePaths = filePaths { + let txtFilePaths = (filePaths.filter { $0.contains(".srt") }).sorted { + guard let str1LastChar = $0.dropLast(4).last, let str2LastChar = $1.dropLast(4).last else { + return false + } + return str1LastChar < str2LastChar + } + for txtFilePath in txtFilePaths { + let subtitleUrl = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: txtFilePath)) + self.subtitleUrls.append(subtitleUrl) + } + } + } + let (all, existing) = NCManageDatabase.shared.getSubtitles(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName) + if !existing.isEmpty { + for subtitle in existing { + let subtitleUrl = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(subtitle.ocId, fileNameView: subtitle.fileName)) + self.subtitleUrls.append(subtitleUrl) + } + } + if all.count != existing.count { + NCContentPresenter.shared.messageNotification("_info_", description: "_subtitle_not_dowloaded_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError) + } + self.setSubtitleToolbarIcon(subtitleUrls: subtitleUrls) + self.hideSubtitle() + } + + func setSubtitleToolbarIcon(subtitleUrls: [URL]) { + if subtitleUrls.isEmpty { + playerToolBar?.subtitleButton.isHidden = true + } else { + playerToolBar?.subtitleButton.isHidden = false + } + } + + func addSubtitlesTo(_ vc: UIViewController, _ playerToolBar: NCPlayerToolBar?) { + addSubtitleLabel(vc, playerToolBar) + NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated(_:)), name: UIDevice.orientationDidChangeNotification, object: nil) + } + + func loadText(filePath: URL, _ completion:@escaping (_ contents: String?) -> Void) { + DispatchQueue.global(qos: .background).async { + guard let data = try? Data(contentsOf: filePath), + let encoding = NCUtility.shared.getEncondingDataType(data: data) else { + return + } + if let decodedString = String(data: data, encoding: encoding) { + completion(decodedString) + } else { + completion(nil) + } + } + } + + func open(fileFromLocal url: URL) { + + subtitleLabel?.text = "" + + self.loadText(filePath: url) { contents in + guard let contents = contents else { + return + } + DispatchQueue.main.async { + self.subtitleLabel?.text = "" + self.show(subtitles: contents) + } + } + } + + @objc public func hideSubtitle() { + self.subtitleLabel?.isHidden = true + self.subtitleContainerView?.isHidden = true + self.currentSubtitle = nil + } + + @objc public func showSubtitle(url: URL) { + self.subtitleLabel?.isHidden = false + self.subtitleContainerView?.isHidden = false + self.currentSubtitle = url + } + + private func show(subtitles string: String) { + parsedPayload = try? NCSubtitles.parseSubRip(string) + if let parsedPayload = parsedPayload { + addPeriodicNotification(parsedPayload: parsedPayload) + } + } + + private func showByDictionary(dictionaryContent: NSMutableDictionary) { + parsedPayload = dictionaryContent + if let parsedPayload = parsedPayload { + addPeriodicNotification(parsedPayload: parsedPayload) + } + } + + func addPeriodicNotification(parsedPayload: NSDictionary) { + // Add periodic notifications + let interval = CMTimeMake(value: 1, timescale: 60) + self.player?.addPeriodicTimeObserver(forInterval: interval, queue: .main) { [weak self] time in + guard let strongSelf = self, let label = strongSelf.subtitleLabel, let containerView = strongSelf.subtitleContainerView else { + return + } + DispatchQueue.main.async { + label.text = NCSubtitles.searchSubtitles(strongSelf.parsedPayload, time.seconds) + strongSelf.adjustViewWidth(containerView: containerView) + strongSelf.adjustLabelHeight(label: label) + } + } + } + + @objc private func deviceRotated(_ notification: Notification) { + guard let label = self.subtitleLabel, + let containerView = self.subtitleContainerView else { return } + DispatchQueue.main.async { + self.adjustViewWidth(containerView: containerView) + self.adjustLabelHeight(label: label) + self.adjustLabelBottom(label: label) + containerView.layoutIfNeeded() + label.layoutIfNeeded() + } + } + + private func adjustLabelHeight(label: UILabel) { + let baseSize = CGSize(width: label.bounds.width, height: .greatestFiniteMagnitude) + let rect = label.sizeThatFits(baseSize) + if label.text != nil { + self.subtitleLabelHeightConstraint?.constant = rect.height + 5.0 + } else { + self.subtitleLabelHeightConstraint?.constant = rect.height + } + } + + private func adjustLabelBottom(label: UILabel) { + var bottomConstant: CGFloat = bottomConstantPortrait + + switch UIApplication.shared.statusBarOrientation { + case .portrait: + bottomConstant = bottomConstantLandscape + case .landscapeLeft, .landscapeRight, .portraitUpsideDown: + bottomConstant = bottomConstantPortrait + default: + () + } + subtitleLabelBottomConstraint?.constant = bottomConstant + } + + private func adjustViewWidth(containerView: UIView) { + let widthConstant: CGFloat = UIScreen.main.bounds.width * widthProportion + subtitleContainerViewWidthConstraint!.constant = widthConstant + subtitleLabel?.preferredMaxLayoutWidth = (widthConstant - 20) + } + + fileprivate func addSubtitleLabel(_ vc: UIViewController, _ playerToolBar: NCPlayerToolBar?) { + guard subtitleLabel == nil, + subtitleContainerView == nil else { + return + } + subtitleContainerView = UIView() + subtitleLabel = UILabel() + + subtitleContainerView?.translatesAutoresizingMaskIntoConstraints = false + subtitleContainerView?.layer.cornerRadius = 5.0 + subtitleContainerView?.layer.masksToBounds = true + subtitleContainerView?.layer.shouldRasterize = true + subtitleContainerView?.layer.rasterizationScale = UIScreen.main.scale + subtitleContainerView?.backgroundColor = UIColor.black.withAlphaComponent(0.35) + + subtitleLabel?.translatesAutoresizingMaskIntoConstraints = false + subtitleLabel?.textAlignment = .center + subtitleLabel?.numberOfLines = 0 + let fontSize = UIDevice.current.userInterfaceIdiom == .pad ? 38.0 : 20.0 + subtitleLabel?.font = UIFont.incosolataMedium(size: fontSize) + subtitleLabel?.lineBreakMode = .byWordWrapping + subtitleLabel?.textColor = .white + subtitleLabel?.backgroundColor = .clear + + subtitleContainerView?.addSubview(subtitleLabel!) + + var isFound = false + + for v in vc.view.subviews where v is UIScrollView { + if let scrollView = v as? UIScrollView { + for subView in scrollView.subviews where subView is imageVideoContainerView { + subView.addSubview(subtitleContainerView!) + isFound = true + break + } + } + } + + if !isFound { + vc.view.addSubview(subtitleContainerView!) + } + + NSLayoutConstraint.activate([ + subtitleLabel!.centerXAnchor.constraint(equalTo: subtitleContainerView!.centerXAnchor), + subtitleLabel!.centerYAnchor.constraint(equalTo: subtitleContainerView!.centerYAnchor) + ]) + + subtitleContainerViewHeightConstraint = NSLayoutConstraint(item: subtitleContainerView!, attribute: .height, relatedBy: .equal, toItem: subtitleLabel!, attribute: .height, multiplier: 1.0, constant: 0.0) + vc.view?.addConstraint(subtitleContainerViewHeightConstraint!) + + var bottomConstant: CGFloat = bottomConstantPortrait + + switch UIApplication.shared.statusBarOrientation { + case .portrait, .portraitUpsideDown: + bottomConstant = bottomConstantLandscape + case .landscapeLeft, .landscapeRight: + bottomConstant = bottomConstantPortrait + default: + () + } + + let widthConstant: CGFloat = UIScreen.main.bounds.width * widthProportion + + NSLayoutConstraint.activate([ + subtitleContainerView!.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor) + ]) + + subtitleContainerViewWidthConstraint = NSLayoutConstraint(item: subtitleContainerView!, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, + attribute: .width, multiplier: 1, constant: widthConstant) + + // setting default width == 0 because there is no text inside of the label + subtitleLabelWidthConstraint = NSLayoutConstraint(item: subtitleLabel!, attribute: .width, relatedBy: .equal, toItem: subtitleContainerView, + attribute: .width, multiplier: 1, constant: -20) + + subtitleLabelBottomConstraint = NSLayoutConstraint(item: subtitleContainerView!, attribute: .bottom, relatedBy: .equal, toItem: vc.view, attribute: + .bottom, multiplier: 1, constant: bottomConstant) + + vc.view?.addConstraint(subtitleContainerViewWidthConstraint!) + vc.view?.addConstraint(subtitleLabelWidthConstraint!) + vc.view?.addConstraint(subtitleLabelBottomConstraint!) + } + + internal func showAlertSubtitles() { + + let alert = UIAlertController(title: nil, message: NSLocalizedString("_subtitle_", comment: ""), preferredStyle: .actionSheet) + + for url in subtitleUrls { + + print("Play Subtitle at:\n\(url.path)") + + let videoUrlTitle = self.metadata.fileName.alphanumeric.dropLast(3) + let subtitleUrlTitle = url.lastPathComponent.alphanumeric.dropLast(3) + + var titleSubtitle = String(subtitleUrlTitle.dropFirst(videoUrlTitle.count)) + if titleSubtitle.isEmpty { + titleSubtitle = NSLocalizedString("_subtitle_", comment: "") + } + + let action = UIAlertAction(title: titleSubtitle, style: .default, handler: { [self] _ in + + if NCUtilityFileSystem.shared.getFileSize(filePath: url.path) > 0 { + + self.open(fileFromLocal: url) + if let viewController = viewController { + self.addSubtitlesTo(viewController, self.playerToolBar) + self.showSubtitle(url: url) + } + + } else { + + let alertError = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_subtitle_not_found_", comment: ""), preferredStyle: .alert) + alertError.addAction(UIKit.UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: nil)) + + viewController?.present(alertError, animated: true, completion: nil) + } + }) + alert.addAction(action) + if currentSubtitle == url { + action.setValue(true, forKey: "checked") + } + } + + let disable = UIAlertAction(title: NSLocalizedString("_disable_", comment: ""), style: .default, handler: { _ in + self.hideSubtitle() + }) + alert.addAction(disable) + if currentSubtitle == nil { + disable.setValue(true, forKey: "checked") + } + + alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in + })) + + alert.popoverPresentationController?.sourceView = self.viewController?.view + + self.viewController?.present(alert, animated: true, completion: nil) + } +} diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitles.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitles.swift new file mode 100644 index 000000000..3fb452420 --- /dev/null +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCSubtitle/NCSubtitles.swift @@ -0,0 +1,150 @@ +// +// NCSubtitles +// Nextcloud +// +// Created by Marc Hervera. +// Copyright 2017 Marc Hervera AVPlayerViewController-Subtitles v1.3.1 iOS +// +// Modified by Federico Malagoni on 23/02/22 for Nextcloud. +// +// Licensed under Apache License v2.0. +// + +import AVKit + +class NCSubtitles { + + // MARK: - Private properties + + private var parsedPayload: NSDictionary? + + // MARK: - Public methods + + public init(file filePath: URL, encoding: String.Encoding = .utf8) throws { + // Get string + let string = try String(contentsOf: filePath, encoding: encoding) + // Parse string + parsedPayload = try NCSubtitles.parseSubRip(string) + } + + public init(subtitles string: String) throws { + // Parse string + parsedPayload = try NCSubtitles.parseSubRip(string) + } + + /// Search subtitles at time + /// + /// - Parameter time: Time + /// - Returns: String if exists + public func searchSubtitles(at time: TimeInterval) -> String? { + return NCSubtitles.searchSubtitles(parsedPayload, time) + } + + // MARK: - Static methods + + /// Subtitle parser + /// + /// - Parameter payload: Input string + /// - Returns: NSDictionary + static func parseSubRip(_ payload: String) throws -> NSDictionary? { + // Prepare payload + var payload = payload.replacingOccurrences(of: "\n\r\n", with: "\n\n") + payload = payload.replacingOccurrences(of: "\n\n\n", with: "\n\n") + payload = payload.replacingOccurrences(of: "\r\n", with: "\n") + + // Parsed dict + let parsed = NSMutableDictionary() + + // Get groups + let regexStr = "(\\d+)\\n([\\d:,.]+)\\s+-{2}\\>\\s+([\\d:,.]+)\\n([\\s\\S]*?(?=\\n{2,}|$))" + let regex = try NSRegularExpression(pattern: regexStr, options: .caseInsensitive) + let matches = regex.matches(in: payload, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: payload.count)) + + for m in matches { + let group = (payload as NSString).substring(with: m.range) + + // Get index + var regex = try NSRegularExpression(pattern: "^[0-9]+", options: .caseInsensitive) + var match = regex.matches(in: group, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: group.count)) + + guard let i = match.first else { + continue + } + + let index = (group as NSString).substring(with: i.range) + + // Get "from" & "to" time + regex = try NSRegularExpression(pattern: "\\d{1,2}:\\d{1,2}:\\d{1,2}[,.]\\d{1,3}", options: .caseInsensitive) + match = regex.matches(in: group, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: group.count)) + + guard match.count == 2 else { + continue + } + + guard let from = match.first, let to = match.last else { + continue + } + + var h: TimeInterval = 0.0, m: TimeInterval = 0.0, s: TimeInterval = 0.0, c: TimeInterval = 0.0 + + let fromStr = (group as NSString).substring(with: from.range) + var scanner = Scanner(string: fromStr) + scanner.scanDouble(&h) + scanner.scanString(":", into: nil) + scanner.scanDouble(&m) + scanner.scanString(":", into: nil) + scanner.scanDouble(&s) + scanner.scanString(",", into: nil) + scanner.scanDouble(&c) + let fromTime = (h * 3600.0) + (m * 60.0) + s + (c / 1000.0) + + let toStr = (group as NSString).substring(with: to.range) + scanner = Scanner(string: toStr) + scanner.scanDouble(&h) + scanner.scanString(":", into: nil) + scanner.scanDouble(&m) + scanner.scanString(":", into: nil) + scanner.scanDouble(&s) + scanner.scanString(",", into: nil) + scanner.scanDouble(&c) + let toTime = (h * 3600.0) + (m * 60.0) + s + (c / 1000.0) + + // Get text & check if empty + let range = NSRange(location: 0, length: to.range.location + to.range.length + 1) + guard (group as NSString).length - range.length > 0 else { + continue + } + + let text = (group as NSString).replacingCharacters(in: range, with: "") + + // Create final object + let final = NSMutableDictionary() + final["from"] = fromTime + final["to"] = toTime + final["text"] = text + parsed[index] = final + } + + return parsed + } + + /// Search subtitle on time + /// + /// - Parameters: + /// - payload: Inout payload + /// - time: Time + /// - Returns: String + static func searchSubtitles(_ payload: NSDictionary?, _ time: TimeInterval) -> String? { + let predicate = NSPredicate(format: "(%f >= %K) AND (%f <= %K)", time, "from", time, "to") + + guard let values = payload?.allValues, let result = (values as NSArray).filtered(using: predicate).first as? NSDictionary else { + return nil + } + + guard let text = result.value(forKey: "text") as? String else { + return nil + } + + return text.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + } +} diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift index f276b5e92..92ffb8ceb 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift @@ -36,12 +36,12 @@ class NCViewerMedia: UIViewController { @IBOutlet weak var statusViewImage: UIImageView! @IBOutlet weak var statusLabel: UILabel! @IBOutlet weak var detailView: NCViewerMediaDetailView! - @IBOutlet weak var playerToolBar: NCPlayerToolBar! private var _autoPlay: Bool = false let appDelegate = UIApplication.shared.delegate as! AppDelegate - var viewerMediaPage: NCViewerMediaPage? + weak var viewerMediaPage: NCViewerMediaPage? + var playerToolBar: NCPlayerToolBar? var ncplayer: NCPlayer? var image: UIImage? var metadata: tableMetadata = tableMetadata() @@ -92,23 +92,33 @@ class NCViewerMedia: UIViewController { statusViewImage.image = nil statusLabel.text = "" } + + if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - playerToolBar.viewerMediaPage = viewerMediaPage + playerToolBar = Bundle.main.loadNibNamed("NCPlayerToolBar", owner: self, options: nil)?.first as? NCPlayerToolBar + if let playerToolBar = playerToolBar { + view.addSubview(playerToolBar) + playerToolBar.translatesAutoresizingMaskIntoConstraints = false + playerToolBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true + playerToolBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true + playerToolBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true + playerToolBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true + playerToolBar.viewerMediaPage = viewerMediaPage + } + let urlVideo = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) + if let url = urlVideo.url { + 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) + } + } + detailViewTopConstraint.constant = 0 detailView.hide() self.image = nil self.imageVideoContainer.image = nil - 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 - } - } + reloadImage() } override func viewWillAppear(_ animated: Bool) { @@ -119,6 +129,10 @@ class NCViewerMedia: UIViewController { if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, let viewerMediaPage = self.viewerMediaPage { viewerMediaPage.currentScreenMode = viewerMediaPage.saveScreenModeImage + if viewerMediaPage.modifiedOcId.contains(metadata.ocId) { + viewerMediaPage.modifiedOcId.removeAll(where: { $0 == metadata.ocId }) + reloadImage() + } } if viewerMediaPage?.currentScreenMode == .full { @@ -148,25 +162,11 @@ class NCViewerMedia: UIViewController { if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - NCKTVHTTPCache.shared.restartProxy(user: appDelegate.user, password: appDelegate.password) - - if ncplayer == nil, let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) { - self.ncplayer = NCPlayer.init(url: url, autoPlay: self.autoPlay, imageVideoContainer: self.imageVideoContainer, playerToolBar: self.playerToolBar, metadata: self.metadata, detailView: self.detailView, viewController: self) - } else { - self.ncplayer?.activateObserver(playerToolBar: self.playerToolBar) - if detailView.isShow() == false && ncplayer?.isPlay() == false { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId": metadata.ocId, "enableTimerAutoHide": false]) - } - } - if let ncplayer = self.ncplayer { + ncplayer.openAVPlayer() self.viewerMediaPage?.updateCommandCenter(ncplayer: ncplayer, metadata: self.metadata) } - #if MFFFLIB - MFFF.shared.setDelegate = self.ncplayer - #endif - } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue { viewerMediaPage?.clearCommandCenter() @@ -196,6 +196,20 @@ class NCViewerMedia: UIViewController { // MARK: - Image + 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 + } + } + } + } + func loadImage(metadata: tableMetadata, completion: @escaping (_ ocId: String, _ image: UIImage?) -> Void) { // Download preview @@ -439,8 +453,13 @@ extension NCViewerMedia { self.detailViewHeighConstraint.constant = 170 } self.view.layoutIfNeeded() - - self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMediaPage?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, ncplayer: self.ncplayer ,delegate: self) + self.detailView.show( + metadata: self.metadata, + image: self.image, + textColor: self.viewerMediaPage?.textColor, + mediaMetadata: (latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel), + ncplayer: self.ncplayer, + delegate: self) if let image = self.imageVideoContainer.image { let ratioW = self.imageVideoContainer.frame.width / image.size.width @@ -460,7 +479,7 @@ extension NCViewerMedia { } self.scrollView.pinchGestureRecognizer?.isEnabled = false - self.playerToolBar.hide() + self.playerToolBar?.hide() } } @@ -487,7 +506,13 @@ extension NCViewerMedia { if self.detailView.isShow() { CCUtility.setExif(metadata) { (latitude, longitude, location, date, lensModel) in - self.detailView.show(metadata:self.metadata, image: self.image, textColor: self.viewerMediaPage?.textColor, latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel, ncplayer: self.ncplayer ,delegate: self) + self.detailView.show( + metadata: self.metadata, + image: self.image, + textColor: self.viewerMediaPage?.textColor, + mediaMetadata: (latitude: latitude, longitude: longitude, location: location, date: date, lensModel: lensModel), + ncplayer: self.ncplayer, + delegate: self) } } } diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift index 350b795cb..15bd22701 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift @@ -25,6 +25,8 @@ import UIKit import MapKit import NCCommunication +typealias NCImageMetadata = (latitude: Double, longitude: Double, location: String?, date: Date?, lensModel: String?) + public protocol NCViewerMediaDetailViewDelegate: AnyObject { func downloadFullResolution() } @@ -52,8 +54,8 @@ class NCViewerMediaDetailView: UIView { var metadata: tableMetadata? var mapView: MKMapView? var ncplayer: NCPlayer? - var delegate: NCViewerMediaDetailViewDelegate? - + weak var delegate: NCViewerMediaDetailViewDelegate? + override func awakeFromNib() { super.awakeFromNib() @@ -76,15 +78,15 @@ class NCViewerMediaDetailView: UIView { self.mapView?.removeFromSuperview() self.mapView = nil } - - func show(metadata: tableMetadata, image: UIImage?, textColor: UIColor?, latitude: Double, longitude: Double, location: String?, date: Date?, lensModel: String?, ncplayer: NCPlayer?, delegate: NCViewerMediaDetailViewDelegate?) { - + + func show(metadata: tableMetadata, image: UIImage?, textColor: UIColor?, mediaMetadata: NCImageMetadata, ncplayer: NCPlayer?, delegate: NCViewerMediaDetailViewDelegate?) { + self.metadata = metadata - self.latitude = latitude - self.longitude = longitude - self.location = location - self.date = date - self.lensModel = lensModel + self.latitude = mediaMetadata.latitude + self.longitude = mediaMetadata.longitude + self.location = mediaMetadata.location + self.date = mediaMetadata.date + self.lensModel = mediaMetadata.lensModel self.ncplayer = ncplayer self.delegate = delegate @@ -93,25 +95,24 @@ class NCViewerMediaDetailView: UIView { let annotation = MKPointAnnotation() annotation.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) - self.mapView = MKMapView() - if let mapView = self.mapView { - mapView.translatesAutoresizingMaskIntoConstraints = false - self.mapContainer.addSubview(mapView) - - NSLayoutConstraint.activate([ - mapView.topAnchor.constraint(equalTo: self.mapContainer.topAnchor), - mapView.bottomAnchor.constraint(equalTo: self.mapContainer.bottomAnchor), - mapView.leadingAnchor.constraint(equalTo: self.mapContainer.leadingAnchor), - mapView.trailingAnchor.constraint(equalTo: self.mapContainer.trailingAnchor) - ]) - - mapView.layer.cornerRadius = 6 - mapView.isZoomEnabled = true - mapView.isScrollEnabled = false - mapView.isUserInteractionEnabled = false - mapView.addAnnotation(annotation) - mapView.setRegion(MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500), animated: false) - } + let mapView = MKMapView() + self.mapView = mapView + self.mapContainer.addSubview(mapView) + + mapView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + mapView.topAnchor.constraint(equalTo: self.mapContainer.topAnchor), + mapView.bottomAnchor.constraint(equalTo: self.mapContainer.bottomAnchor), + mapView.leadingAnchor.constraint(equalTo: self.mapContainer.leadingAnchor), + mapView.trailingAnchor.constraint(equalTo: self.mapContainer.trailingAnchor) + ]) + + mapView.layer.cornerRadius = 6 + mapView.isZoomEnabled = true + mapView.isScrollEnabled = false + mapView.isUserInteractionEnabled = false + mapView.addAnnotation(annotation) + mapView.setRegion(MKCoordinateRegion(center: annotation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500), animated: false) } // Size @@ -156,7 +157,7 @@ class NCViewerMediaDetailView: UIView { } // Message - if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata) && metadata.session == "" { + if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata) && metadata.session.isEmpty { messageButton.setTitle(NSLocalizedString("_try_download_full_resolution_", comment: ""), for: .normal) messageButton.isHidden = false } else { @@ -188,23 +189,22 @@ class NCViewerMediaDetailView: UIView { @IBAction func touchLocation(_ sender: Any) { - if latitude != -1 && latitude != 0 && longitude != -1 && longitude != 0 { - - let latitude: CLLocationDegrees = self.latitude - let longitude: CLLocationDegrees = self.longitude - - let regionDistance: CLLocationDistance = 10000 - let coordinates = CLLocationCoordinate2DMake(latitude, longitude) - let regionSpan = MKCoordinateRegion(center: coordinates, latitudinalMeters: regionDistance, longitudinalMeters: regionDistance) - let options = [ - MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center), - MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span) - ] - let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil) - let mapItem = MKMapItem(placemark: placemark) - mapItem.name = location - mapItem.openInMaps(launchOptions: options) - } + guard latitude != -1, latitude != 0, longitude != -1, longitude != 0 else { return } + + let latitude: CLLocationDegrees = self.latitude + let longitude: CLLocationDegrees = self.longitude + + let regionDistance: CLLocationDistance = 10000 + let coordinates = CLLocationCoordinate2DMake(latitude, longitude) + let regionSpan = MKCoordinateRegion(center: coordinates, latitudinalMeters: regionDistance, longitudinalMeters: regionDistance) + let options = [ + MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center), + MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span) + ] + let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil) + let mapItem = MKMapItem(placemark: placemark) + mapItem.name = location + mapItem.openInMaps(launchOptions: options) } @IBAction func touchFavorite(_ sender: Any) { @@ -212,12 +212,6 @@ class NCViewerMediaDetailView: UIView { } @IBAction func touchMessage(_ sender: Any) { - delegate?.downloadFullResolution() } - - // MARK: - - func secondsToHoursMinutesSeconds (seconds: Int) -> (Int, Int, Int) { - return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60) - } } diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard index 8e3d04ebb..329daf6cc 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard @@ -4,7 +4,6 @@ <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> - <capability name="Image references" minToolsVersion="12.0"/> <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"/> @@ -109,6 +108,10 @@ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dJP-ZX-iug"> <rect key="frame" x="15" y="150" width="384" height="222"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <gestureRecognizers/> + <connections> + <outletCollection property="gestureRecognizers" destination="fvW-pC-4g1" appends="YES" id="gVL-xL-CmY"/> + </connections> </view> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="size" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WXS-Lw-DkI"> <rect key="frame" x="15" y="36" width="80" height="16"/> @@ -242,159 +245,6 @@ <outlet property="sizeValue" destination="XLb-0a-du9" id="9jm-Ku-sgt"/> </connections> </view> - <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dgJ-dQ-lSp" userLabel="Player Top Tool Bar"> - <rect key="frame" x="299" y="10" width="100" height="45"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9aW-Pg-83x" userLabel="Button Player Top Tool Bar"> - <rect key="frame" x="0.0" y="0.0" width="100" height="45"/> - <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> - <connections> - <action selector="buttonPlayerTopToolBarTouchInside:" destination="sBp-t2-eFh" eventType="touchUpInside" id="Afs-Nt-FaQ"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8AB-hx-yqN" userLabel="Mute Button"> - <rect key="frame" x="60" y="10" width="25" height="25"/> - <constraints> - <constraint firstAttribute="width" constant="25" id="6yU-23-dkA"/> - <constraint firstAttribute="height" constant="25" id="hBk-Ku-BAd"/> - </constraints> - <state key="normal" image="audioOn"/> - <connections> - <action selector="setMute:" destination="sBp-t2-eFh" eventType="touchUpInside" id="cVl-BA-mPJ"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NwE-zQ-Y5D" userLabel="Mute Button"> - <rect key="frame" x="15" y="10" width="25" height="25"/> - <constraints> - <constraint firstAttribute="width" constant="25" id="CaQ-c0-MES"/> - <constraint firstAttribute="height" constant="25" id="N06-qe-ZVY"/> - </constraints> - <state key="normal" image="pip.enter" catalog="system"/> - <connections> - <action selector="setPip:" destination="sBp-t2-eFh" eventType="touchUpInside" id="svR-8R-DQY"/> - </connections> - </button> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="trailing" secondItem="9aW-Pg-83x" secondAttribute="trailing" id="8db-gr-hzG"/> - <constraint firstItem="NwE-zQ-Y5D" firstAttribute="centerY" secondItem="dgJ-dQ-lSp" secondAttribute="centerY" id="Ad6-Aj-hvc"/> - <constraint firstAttribute="bottom" secondItem="9aW-Pg-83x" secondAttribute="bottom" id="B8A-Zh-HYL"/> - <constraint firstAttribute="width" constant="100" id="LTs-QZ-nVw"/> - <constraint firstAttribute="trailing" secondItem="8AB-hx-yqN" secondAttribute="trailing" constant="15" id="bQK-xA-Jyu"/> - <constraint firstItem="NwE-zQ-Y5D" firstAttribute="leading" secondItem="dgJ-dQ-lSp" secondAttribute="leading" constant="15" id="cZY-tl-BOh"/> - <constraint firstItem="9aW-Pg-83x" firstAttribute="leading" secondItem="dgJ-dQ-lSp" secondAttribute="leading" id="clL-y7-JuR"/> - <constraint firstItem="9aW-Pg-83x" firstAttribute="top" secondItem="dgJ-dQ-lSp" secondAttribute="top" id="jQP-bV-BQ9"/> - <constraint firstItem="8AB-hx-yqN" firstAttribute="centerY" secondItem="dgJ-dQ-lSp" secondAttribute="centerY" id="uNT-D4-mK3"/> - <constraint firstAttribute="height" constant="45" id="xf9-B5-HMs"/> - </constraints> - </view> - <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sBp-t2-eFh" customClass="NCPlayerToolBar" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="15" y="661" width="384" height="65"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a0D-B0-eGX" userLabel="Button Player Tool Bar"> - <rect key="frame" x="0.0" y="0.0" width="384" height="65"/> - <connections> - <action selector="buttonPlayerToolBarTouchInside:" destination="sBp-t2-eFh" eventType="touchUpInside" id="hyl-lR-OGD"/> - </connections> - </button> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Z2D-bl-6Qu" userLabel="Container play"> - <rect key="frame" x="0.0" y="0.0" width="115.33333333333333" height="65"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Isy-sX-Gji"> - <rect key="frame" x="3" y="12.666666666666629" width="40" height="40"/> - <constraints> - <constraint firstAttribute="width" constant="40" id="COm-r2-ubw"/> - <constraint firstAttribute="height" constant="40" id="mbb-ql-zCc"/> - </constraints> - <state key="normal" image="gobackward.10" catalog="system"/> - <connections> - <action selector="backButtonSec:" destination="sBp-t2-eFh" eventType="touchUpInside" id="Q46-Tp-inF"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x3E-b2-obf"> - <rect key="frame" x="37.666666666666629" y="12.666666666666629" width="40" height="40"/> - <constraints> - <constraint firstAttribute="width" constant="40" id="Cmv-LX-Phg"/> - <constraint firstAttribute="height" constant="40" id="djE-Ml-YD0"/> - </constraints> - <state key="normal"> - <imageReference key="image" image="play.fill" catalog="system" symbolScale="default"/> - <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/> - </state> - <connections> - <action selector="playerPause:" destination="sBp-t2-eFh" eventType="touchUpInside" id="pRl-bT-hpi"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F9L-PP-AbM"> - <rect key="frame" x="72" y="12.666666666666629" width="40" height="40"/> - <constraints> - <constraint firstAttribute="width" constant="40" id="hlL-XQ-OLa"/> - <constraint firstAttribute="height" constant="40" id="vEe-Fb-a9N"/> - </constraints> - <state key="normal" image="goforward.10" catalog="system"/> - <connections> - <action selector="forwardButtonSec:" destination="sBp-t2-eFh" eventType="touchUpInside" id="QPi-TQ-rNF"/> - </connections> - </button> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="F9L-PP-AbM" firstAttribute="centerY" secondItem="Z2D-bl-6Qu" secondAttribute="centerY" id="Hb6-zo-b2N"/> - <constraint firstItem="x3E-b2-obf" firstAttribute="centerX" secondItem="Z2D-bl-6Qu" secondAttribute="centerX" id="Hj0-C7-K75"/> - <constraint firstItem="F9L-PP-AbM" firstAttribute="centerX" secondItem="Z2D-bl-6Qu" secondAttribute="centerX" multiplier="1.6" id="a8O-oF-jJM"/> - <constraint firstItem="Isy-sX-Gji" firstAttribute="centerX" secondItem="Z2D-bl-6Qu" secondAttribute="centerX" multiplier="0.4" id="mLO-jA-XH6"/> - <constraint firstItem="Isy-sX-Gji" firstAttribute="centerY" secondItem="Z2D-bl-6Qu" secondAttribute="centerY" id="ngz-yI-ozW"/> - <constraint firstItem="x3E-b2-obf" firstAttribute="centerY" secondItem="Z2D-bl-6Qu" secondAttribute="centerY" id="qMZ-ZT-IWE"/> - </constraints> - </view> - <slider opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="SR4-e8-1hC"> - <rect key="frame" x="118.33333333333331" y="17.666666666666629" width="257.66666666666663" height="31"/> - </slider> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="99:99:99" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OUD-HH-cXH"> - <rect key="frame" x="328.66666666666669" y="47.666666666666629" width="45.333333333333314" height="12"/> - <fontDescription key="fontDescription" type="system" pointSize="10"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00:00" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kVv-X4-6SK"> - <rect key="frame" x="120.33333333333331" y="47.666666666666629" width="45" height="12"/> - <fontDescription key="fontDescription" type="system" pointSize="10"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="Z2D-bl-6Qu" firstAttribute="width" secondItem="sBp-t2-eFh" secondAttribute="width" multiplier="0.3" id="13g-iK-lwr"/> - <constraint firstItem="kVv-X4-6SK" firstAttribute="leading" secondItem="SR4-e8-1hC" secondAttribute="leading" id="5hb-O9-gEt"/> - <constraint firstItem="SR4-e8-1hC" firstAttribute="leading" secondItem="Z2D-bl-6Qu" secondAttribute="trailing" constant="5" id="6He-ZU-Vwa"/> - <constraint firstItem="Z2D-bl-6Qu" firstAttribute="height" secondItem="sBp-t2-eFh" secondAttribute="height" id="AbA-kH-vt3"/> - <constraint firstAttribute="height" constant="65" id="EyI-HW-pHA"/> - <constraint firstItem="SR4-e8-1hC" firstAttribute="centerY" secondItem="sBp-t2-eFh" secondAttribute="centerY" id="L8n-9C-y6b"/> - <constraint firstItem="a0D-B0-eGX" firstAttribute="leading" secondItem="sBp-t2-eFh" secondAttribute="leading" id="OIL-1t-uuh"/> - <constraint firstAttribute="trailing" secondItem="SR4-e8-1hC" secondAttribute="trailing" constant="10" id="Q3r-ex-Cpf"/> - <constraint firstAttribute="bottom" secondItem="a0D-B0-eGX" secondAttribute="bottom" id="a3T-pV-xrr"/> - <constraint firstItem="Z2D-bl-6Qu" firstAttribute="centerY" secondItem="sBp-t2-eFh" secondAttribute="centerY" id="gdD-MC-K1m"/> - <constraint firstItem="a0D-B0-eGX" firstAttribute="top" secondItem="sBp-t2-eFh" secondAttribute="top" id="itu-re-FU4"/> - <constraint firstAttribute="trailing" secondItem="OUD-HH-cXH" secondAttribute="trailing" constant="10" id="j5v-RG-0JI"/> - <constraint firstItem="Z2D-bl-6Qu" firstAttribute="leading" secondItem="sBp-t2-eFh" secondAttribute="leading" id="jHq-yM-afK"/> - <constraint firstItem="kVv-X4-6SK" firstAttribute="top" secondItem="SR4-e8-1hC" secondAttribute="bottom" id="l6a-l5-ZvL"/> - <constraint firstAttribute="trailing" secondItem="a0D-B0-eGX" secondAttribute="trailing" id="lbh-DN-SZF"/> - <constraint firstItem="OUD-HH-cXH" firstAttribute="top" secondItem="SR4-e8-1hC" secondAttribute="bottom" id="wwk-tz-5dj"/> - </constraints> - <connections> - <outlet property="backButton" destination="Isy-sX-Gji" id="RBh-0O-yAN"/> - <outlet property="forwardButton" destination="F9L-PP-AbM" id="cc0-4F-r5v"/> - <outlet property="labelCurrentTime" destination="kVv-X4-6SK" id="vyf-Bb-TPL"/> - <outlet property="labelLeftTime" destination="OUD-HH-cXH" id="MjW-XN-Uer"/> - <outlet property="muteButton" destination="8AB-hx-yqN" id="9zQ-k7-auv"/> - <outlet property="pipButton" destination="NwE-zQ-Y5D" id="veJ-QD-fOd"/> - <outlet property="playButton" destination="x3E-b2-obf" id="0Nw-L4-W7M"/> - <outlet property="playbackSlider" destination="SR4-e8-1hC" id="Khx-Oe-NEQ"/> - <outlet property="playerTopToolBarView" destination="dgJ-dQ-lSp" id="22g-Yn-k5r"/> - </connections> - </view> </subviews> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> @@ -413,19 +263,14 @@ <viewLayoutGuide key="safeArea" id="Yo6-7W-moG"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> - <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="dgJ-dQ-lSp" secondAttribute="trailing" constant="15" id="15m-hD-wYt"/> <constraint firstAttribute="bottom" secondItem="CdQ-LC-Trx" secondAttribute="bottom" id="4qB-8y-OcG"/> <constraint firstAttribute="trailing" secondItem="CdQ-LC-Trx" secondAttribute="trailing" id="IwE-oE-d3Y"/> - <constraint firstItem="Yo6-7W-moG" firstAttribute="bottom" secondItem="sBp-t2-eFh" secondAttribute="bottom" constant="10" id="QHF-av-zeT"/> - <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="sBp-t2-eFh" secondAttribute="trailing" constant="15" id="TCr-e0-gnG"/> <constraint firstItem="2AU-85-K8y" firstAttribute="leading" secondItem="Yo6-7W-moG" secondAttribute="leading" constant="10" id="X10-OG-EKg"/> <constraint firstItem="Yo6-7W-moG" firstAttribute="top" secondItem="2AU-85-K8y" secondAttribute="top" constant="-10" id="avO-83-uMQ"/> <constraint firstItem="Yo6-7W-moG" firstAttribute="bottom" secondItem="P8R-4f-zAl" secondAttribute="top" constant="400" id="bor-cg-Alz"/> <constraint firstItem="P8R-4f-zAl" firstAttribute="leading" secondItem="Yo6-7W-moG" secondAttribute="leading" id="dly-i5-fPW"/> - <constraint firstItem="dgJ-dQ-lSp" firstAttribute="top" secondItem="Yo6-7W-moG" secondAttribute="top" constant="10" id="g7l-5Z-sJj"/> <constraint firstItem="CdQ-LC-Trx" firstAttribute="leading" secondItem="fIE-H6-KKc" secondAttribute="leading" id="g8C-2m-KkX"/> <constraint firstItem="CdQ-LC-Trx" firstAttribute="top" secondItem="fIE-H6-KKc" secondAttribute="top" id="hcQ-lB-JwU"/> - <constraint firstItem="sBp-t2-eFh" firstAttribute="leading" secondItem="Yo6-7W-moG" secondAttribute="leading" constant="15" id="hwP-QY-nRI"/> <constraint firstItem="Yo6-7W-moG" firstAttribute="trailing" secondItem="P8R-4f-zAl" secondAttribute="trailing" id="jf2-Nv-gFi"/> </constraints> </view> @@ -436,24 +281,23 @@ <outlet property="imageVideoContainer" destination="kPV-JM-UnM" id="2pA-VW-FuK"/> <outlet property="imageViewBottomConstraint" destination="vEd-X2-yGs" id="wp3-67-aZ2"/> <outlet property="imageViewTopConstraint" destination="tdo-XY-uqv" id="AM2-tz-fSt"/> - <outlet property="playerToolBar" destination="sBp-t2-eFh" id="E24-0a-bN6"/> <outlet property="scrollView" destination="CdQ-LC-Trx" id="3np-FR-s39"/> <outlet property="statusLabel" destination="DAi-gz-qGP" id="zZH-1B-HiI"/> <outlet property="statusViewImage" destination="2AU-85-K8y" id="22h-Ec-bj0"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="fbE-Jv-mLH" userLabel="First Responder" sceneMemberID="firstResponder"/> + <tapGestureRecognizer id="fvW-pC-4g1"> + <connections> + <action selector="touchLocation:" destination="P8R-4f-zAl" id="qyd-d2-RyB"/> + </connections> + </tapGestureRecognizer> </objects> <point key="canvasLocation" x="4547.826086956522" y="776.9021739130435"/> </scene> </scenes> <resources> - <image name="audioOn" width="28" height="28"/> - <image name="gobackward.10" catalog="system" width="121" height="128"/> - <image name="goforward.10" catalog="system" width="121" height="128"/> <image name="networkInProgress" width="300" height="300"/> - <image name="pip.enter" catalog="system" width="128" height="96"/> - <image name="play.fill" catalog="system" width="116" height="128"/> <systemColor name="systemBackgroundColor"> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> </systemColor> diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift index 5a304f6ea..253073658 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift @@ -44,6 +44,7 @@ class NCViewerMediaPage: UIViewController { } var metadatas: [tableMetadata] = [] + var modifiedOcId: [String] = [] var currentIndex = 0 var nextIndex: Int? var ncplayerLivePhoto: NCPlayer? @@ -96,6 +97,8 @@ 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(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) @@ -104,24 +107,24 @@ class NCViewerMediaPage: UIViewController { 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) - // Clear if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() { ncplayer.playerPause() ncplayer.saveCurrentTime() } - currentViewController.playerToolBar.stopTimerAutoHide() + currentViewController.playerToolBar?.stopTimerAutoHide() clearCommandCenter() metadatas.removeAll() ncplayerLivePhoto = nil - - #if MFFFLIB - MFFF.shared.dismissMessage() - #endif - + // 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) @@ -130,6 +133,8 @@ 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.notificationCenterUploadedFile), object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil) @@ -147,6 +152,10 @@ class NCViewerMediaPage: UIViewController { } } + override var prefersHomeIndicatorAutoHidden: Bool { + return currentScreenMode == .full + } + // MARK: - func getViewerMedia(index: Int, metadata: tableMetadata) -> NCViewerMedia { @@ -180,7 +189,7 @@ class NCViewerMediaPage: UIViewController { progressView.isHidden = false if !currentViewController.detailView.isShow() { - currentViewController.playerToolBar.show(enableTimerAutoHide: enableTimerAutoHide) + currentViewController.playerToolBar?.show(enableTimerAutoHide: enableTimerAutoHide) } NCUtility.shared.colorNavigationController(navigationController, backgroundColor: NCBrandColor.shared.systemBackground, titleColor: NCBrandColor.shared.label, tintColor: nil, withoutShadow: false) @@ -192,7 +201,7 @@ class NCViewerMediaPage: UIViewController { navigationController?.setNavigationBarHidden(true, animated: true) progressView.isHidden = true - currentViewController.playerToolBar.hide() + currentViewController.playerToolBar?.hide() view.backgroundColor = .black textColor = .white @@ -205,6 +214,7 @@ class NCViewerMediaPage: UIViewController { } setNeedsStatusBarAppearanceUpdate() + setNeedsUpdateOfHomeIndicatorAutoHidden() currentViewController.reloadDetail() } @@ -229,6 +239,22 @@ class NCViewerMediaPage: UIViewController { } } + @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) + } + } + } + } + } + @objc func deleteFile(_ notification: NSNotification) { if let userInfo = notification.userInfo as NSDictionary? { @@ -344,7 +370,7 @@ class NCViewerMediaPage: UIViewController { skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand.addTarget { event in let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval) - self.currentViewController.playerToolBar.skip(seconds: seconds) + self.currentViewController.playerToolBar?.skip(seconds: seconds) return.success } @@ -352,7 +378,7 @@ class NCViewerMediaPage: UIViewController { skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand.addTarget { event in let seconds = Float64((event as! MPSkipIntervalCommandEvent).interval) - self.currentViewController.playerToolBar.skip(seconds: -seconds) + self.currentViewController.playerToolBar?.skip(seconds: -seconds) return.success } } @@ -444,16 +470,12 @@ extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerD direction = .reverse } - currentViewController.ncplayer?.deactivateObserver() - let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex]) pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil) } func reloadCurrentPage() { - - currentViewController.ncplayer?.deactivateObserver() - + let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex]) viewerMedia.autoPlay = false pageViewController.setViewControllers([viewerMedia], direction: .forward, animated: false, completion: nil) @@ -463,8 +485,6 @@ extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerD currentIndex = index - currentViewController.ncplayer?.deactivateObserver() - let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex]) viewerMedia.autoPlay = autoPlay pageViewController.setViewControllers([viewerMedia], direction: direction, animated: true, completion: nil) @@ -474,15 +494,15 @@ extension NCViewerMediaPage: UIPageViewControllerDelegate, UIPageViewControllerD if currentIndex == 0 { return nil } - let viewerMedia = getViewerMedia(index: currentIndex-1, metadata: metadatas[currentIndex-1]) + let viewerMedia = getViewerMedia(index: currentIndex - 1, metadata: metadatas[currentIndex - 1]) return viewerMedia } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { - if currentIndex == metadatas.count-1 { return nil } + if currentIndex == metadatas.count - 1 { return nil } - let viewerMedia = getViewerMedia(index: currentIndex+1, metadata: metadatas[currentIndex+1]) + let viewerMedia = getViewerMedia(index: currentIndex + 1, metadata: metadatas[currentIndex + 1]) return viewerMedia } @@ -587,8 +607,11 @@ extension NCViewerMediaPage: UIGestureRecognizerDelegate { AudioServicesPlaySystemSound(1519) // peek feedback - if let url = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) { - self.ncplayerLivePhoto = NCPlayer.init(url: url, autoPlay: true, imageVideoContainer: self.currentViewController.imageVideoContainer, playerToolBar: nil, metadata: metadata, detailView: nil, viewController: self) + let urlVideo = NCKTVHTTPCache.shared.getVideoURL(metadata: metadata) + + if let url = urlVideo.url { + self.ncplayerLivePhoto = NCPlayer.init(url: url, autoPlay: true, isProxy: urlVideo.isProxy, imageVideoContainer: self.currentViewController.imageVideoContainer, playerToolBar: nil, metadata: metadata, detailView: nil, viewController: self) + self.ncplayerLivePhoto?.openAVPlayer() } } diff --git a/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift b/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift index a567e3ed2..f44cd3018 100644 --- a/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift +++ b/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift @@ -72,9 +72,9 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes if editor == NCGlobal.shared.editorOnlyoffice { webView.customUserAgent = NCUtility.shared.getCustomUserAgentOnlyOffice() - } else { - webView.customUserAgent = CCUtility.getUserAgent() - } + } else if editor == NCGlobal.shared.editorText { + webView.customUserAgent = NCUtility.shared.getCustomUserAgentNCText() + } // else: use default webView.load(request) } diff --git a/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift b/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift index c0884fa77..a188fcb93 100644 --- a/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift +++ b/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift @@ -23,21 +23,34 @@ import UIKit import PDFKit +import EasyTipView class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { - let appDelegate = UIApplication.shared.delegate as! AppDelegate var metadata = tableMetadata() var imageIcon: UIImage? + private var filePath = "" + private var pdfView = PDFView() - private var thumbnailViewHeight: CGFloat = 40 + private var pdfThumbnailScrollView = UIScrollView() private var pdfThumbnailView = PDFThumbnailView() private var pdfDocument: PDFDocument? private let pageView = UIView() private let pageViewLabel = UILabel() + private var tipView: EasyTipView? + + private let thumbnailViewHeight: CGFloat = 70 + private let thumbnailViewWidth: CGFloat = 80 + private let thumbnailPadding: CGFloat = 2 + private let animateDuration: TimeInterval = 0.3 + + private var defaultBackgroundColor: UIColor = .clear + + private var pdfThumbnailScrollViewTopAnchor: NSLayoutConstraint? + private var pdfThumbnailScrollViewTrailingAnchor: NSLayoutConstraint? + private var pdfThumbnailScrollViewWidthAnchor: NSLayoutConstraint? private var pageViewWidthAnchor: NSLayoutConstraint? - private var filePath = "" // MARK: - View Life Cycle @@ -48,78 +61,131 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { override func viewDidLoad() { filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! - - pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)) pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath)) + let pageCount = CGFloat(pdfDocument?.pageCount ?? 0) + if #available(iOS 13.0, *) { + defaultBackgroundColor = pdfView.backgroundColor + } else { + defaultBackgroundColor = .lightGray + } + view.backgroundColor = defaultBackgroundColor + + navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore)) + navigationItem.title = metadata.fileNameView + // PDF VIEW + + if UIDevice.current.userInterfaceIdiom == .phone { + pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)) + } else { + pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: view.frame.width-thumbnailViewWidth, height: view.frame.height)) + } + pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin] pdfView.document = pdfDocument - pdfView.backgroundColor = NCBrandColor.shared.systemBackground - pdfView.displayMode = .singlePageContinuous pdfView.autoScales = true - pdfView.displayDirection = CCUtility.getPDFDisplayDirection() - pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleBottomMargin] - pdfView.usePageViewController(true, withViewOptions: nil) - + pdfView.displayMode = .singlePageContinuous + pdfView.displayDirection = .vertical + pdfView.maxScaleFactor = 4.0 + pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit view.addSubview(pdfView) + // PDF THUMBNAIL + + pdfThumbnailScrollView.translatesAutoresizingMaskIntoConstraints = false + pdfThumbnailScrollView.backgroundColor = defaultBackgroundColor + pdfThumbnailScrollView.showsVerticalScrollIndicator = false + view.addSubview(pdfThumbnailScrollView) + + pdfThumbnailScrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor) + pdfThumbnailScrollViewTopAnchor?.isActive = true + pdfThumbnailScrollViewTrailingAnchor = pdfThumbnailScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor) + pdfThumbnailScrollViewTrailingAnchor?.isActive = true + pdfThumbnailScrollViewWidthAnchor = pdfThumbnailScrollView.widthAnchor.constraint(equalToConstant: thumbnailViewWidth) + pdfThumbnailScrollViewWidthAnchor?.isActive = true + pdfThumbnailView.translatesAutoresizingMaskIntoConstraints = false pdfThumbnailView.pdfView = pdfView - pdfThumbnailView.layoutMode = .horizontal - pdfThumbnailView.thumbnailSize = CGSize(width: 40, height: thumbnailViewHeight) + pdfThumbnailView.layoutMode = .vertical + pdfThumbnailView.thumbnailSize = CGSize(width: thumbnailViewHeight, height: thumbnailViewHeight) pdfThumbnailView.backgroundColor = .clear - // pdfThumbnailView.layer.shadowOffset.height = -5 - // pdfThumbnailView.layer.shadowOpacity = 0.25 - - view.addSubview(pdfThumbnailView) - - pdfThumbnailView.heightAnchor.constraint(equalToConstant: thumbnailViewHeight).isActive = true - pdfThumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true - pdfThumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true - pdfThumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true + if UIDevice.current.userInterfaceIdiom == .phone { + self.pdfThumbnailScrollView.isHidden = true + } else { + self.pdfThumbnailScrollView.isHidden = false + } + pdfThumbnailScrollView.addSubview(pdfThumbnailView) + + NSLayoutConstraint.activate([ + pdfThumbnailView.topAnchor.constraint(equalTo: pdfThumbnailScrollView.topAnchor), + pdfThumbnailView.bottomAnchor.constraint(equalTo: pdfThumbnailScrollView.bottomAnchor), + pdfThumbnailView.leadingAnchor.constraint(equalTo: pdfThumbnailScrollView.leadingAnchor), + pdfThumbnailView.leadingAnchor.constraint(equalTo: pdfThumbnailScrollView.trailingAnchor, constant: (UIApplication.shared.keyWindow?.safeAreaInsets.left ?? 0)), + pdfThumbnailView.widthAnchor.constraint(equalToConstant: thumbnailViewWidth) + ]) + let contentViewCenterY = pdfThumbnailView.centerYAnchor.constraint(equalTo: pdfThumbnailScrollView.centerYAnchor) + contentViewCenterY.priority = .defaultLow + let contentViewHeight = pdfThumbnailView.heightAnchor.constraint(equalToConstant: CGFloat(pageCount * thumbnailViewHeight) + CGFloat(pageCount * thumbnailPadding) + 30) + contentViewHeight.priority = .defaultLow + NSLayoutConstraint.activate([ + contentViewCenterY, + contentViewHeight + ]) + + // COUNTER PDF PAGE VIEW pageView.translatesAutoresizingMaskIntoConstraints = false pageView.layer.cornerRadius = 10 - pageView.backgroundColor = UIColor.gray.withAlphaComponent(0.3) - + pageView.backgroundColor = NCBrandColor.shared.systemBackground.withAlphaComponent( + UIAccessibility.isReduceTransparencyEnabled ? 1 : 0.5 + ) view.addSubview(pageView) - pageView.heightAnchor.constraint(equalToConstant: 30).isActive = true + NSLayoutConstraint.activate([ + pageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), + pageView.heightAnchor.constraint(equalToConstant: 30), + pageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 10) + ]) pageViewWidthAnchor = pageView.widthAnchor.constraint(equalToConstant: 10) pageViewWidthAnchor?.isActive = true - pageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 4).isActive = true - pageView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 7).isActive = true pageViewLabel.translatesAutoresizingMaskIntoConstraints = false pageViewLabel.textAlignment = .center - pageViewLabel.textColor = .gray - + pageViewLabel.textColor = NCBrandColor.shared.label pageView.addSubview(pageViewLabel) - pageViewLabel.leftAnchor.constraint(equalTo: pageView.leftAnchor).isActive = true - pageViewLabel.rightAnchor.constraint(equalTo: pageView.rightAnchor).isActive = true - pageViewLabel.topAnchor.constraint(equalTo: pageView.topAnchor).isActive = true - pageViewLabel.bottomAnchor.constraint(equalTo: pageView.bottomAnchor).isActive = true + NSLayoutConstraint.activate([ + pageViewLabel.topAnchor.constraint(equalTo: pageView.topAnchor), + pageViewLabel.leftAnchor.constraint(equalTo: pageView.leftAnchor), + pageViewLabel.rightAnchor.constraint(equalTo: pageView.rightAnchor), + pageViewLabel.bottomAnchor.constraint(equalTo: pageView.bottomAnchor) + ]) - pdfView.backgroundColor = NCBrandColor.shared.systemBackground - pdfView.layoutIfNeeded() - handlePageChange() + // GESTURE - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:))) - tapGesture.numberOfTapsRequired = 1 - pdfView.addGestureRecognizer(tapGesture) + let tapPdfView = UITapGestureRecognizer(target: self, action: #selector(tapPdfView)) + tapPdfView.numberOfTapsRequired = 1 + pdfView.addGestureRecognizer(tapPdfView) // recognize single / double tap for gesture in pdfView.gestureRecognizers! { - tapGesture.require(toFail: gesture) + tapPdfView.require(toFail: gesture) } - } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) + let swipePdfView = UISwipeGestureRecognizer(target: self, action: #selector(gestureClosePdfThumbnail)) + swipePdfView.direction = .right + swipePdfView.delegate = self + pdfView.addGestureRecognizer(swipePdfView) - appDelegate.activeViewController = self + let edgePdfView = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(gestureOpenPdfThumbnail)) + edgePdfView.edges = .right + edgePdfView.delegate = self + pdfView.addGestureRecognizer(edgePdfView) + + let swipePdfThumbnailScrollView = UISwipeGestureRecognizer(target: self, action: #selector(gestureClosePdfThumbnail)) + swipePdfThumbnailScrollView.direction = .right + pdfThumbnailScrollView.addGestureRecognizer(swipePdfThumbnailScrollView) - // NotificationCenter.default.addObserver(self, selector: #selector(favoriteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil) 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) @@ -128,19 +194,62 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(searchText), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuSearchTextPDF), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(direction(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(goToPage), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuGotToPageInPDF), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handlePageChange), name: Notification.Name.PDFViewPageChanged, object: nil) - // - navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "more")!.image(color: NCBrandColor.shared.label, size: 25), style: .plain, target: self, action: #selector(self.openMenuMore)) + // Tip + if UIDevice.current.userInterfaceIdiom == .phone && !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCViewerPDFThumbnail){ - navigationController?.navigationBar.prefersLargeTitles = true - navigationItem.title = metadata.fileNameView + var preferences = EasyTipView.Preferences() + preferences.drawing.foregroundColor = .white + preferences.drawing.backgroundColor = NCBrandColor.shared.nextcloud + preferences.drawing.textAlignment = .left + preferences.drawing.arrowPosition = .right + preferences.drawing.cornerRadius = 10 + + preferences.positioning.bubbleInsets.right = UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0 + + 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_pdf_thumbnails_", comment: ""), preferences: preferences, delegate: self) + } + + setConstraints() + handlePageChange() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.tipView?.show(forView: self.pdfThumbnailScrollView, withinSuperview: self.view) + } + + @objc func viewUnload() { + + navigationController?.popViewController(animated: true) + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate(alongsideTransition: { context in + if UIDevice.current.userInterfaceIdiom == .phone { + // Close + self.tipView?.dismiss() + self.pdfThumbnailScrollViewTrailingAnchor?.constant = self.thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0) + self.pdfThumbnailScrollView.isHidden = true + } + }, completion: { context in + self.setConstraints() + }) } - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) + deinit { NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil) @@ -150,17 +259,11 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuSearchTextPDF), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuPDFDisplayDirection), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuGotToPageInPDF), object: nil) NotificationCenter.default.removeObserver(self, name: Notification.Name.PDFViewPageChanged, object: nil) } - @objc func viewUnload() { - - navigationController?.popViewController(animated: true) - } - // MARK: - NotificationCenter @objc func uploadedFile(_ notification: NSNotification) { @@ -234,17 +337,6 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { self.present(navigaionController, animated: true) } - @objc func direction(_ notification: NSNotification) { - - if let userInfo = notification.userInfo as NSDictionary? { - if let direction = userInfo["direction"] as? PDFDisplayDirection { - pdfView.displayDirection = direction - CCUtility.setPDFDisplayDirection(direction) - handlePageChange() - } - } - } - @objc func goToPage() { guard let pdfDocument = pdfView.document else { return } @@ -270,72 +362,178 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { // MARK: - Action @objc func openMenuMore() { + if imageIcon == nil { imageIcon = UIImage(named: "file_pdf") } NCViewer.shared.toggleMenu(viewController: self, metadata: metadata, webView: false, imageIcon: imageIcon) } // MARK: - Gesture Recognizer - @objc func didTap(_ recognizer: UITapGestureRecognizer) { + @objc func tapPdfView(_ recognizer: UITapGestureRecognizer) { - if navigationController?.isNavigationBarHidden ?? false { + pdfThumbnailScrollViewTopAnchor?.isActive = false - navigationController?.setNavigationBarHidden(false, animated: false) - pdfThumbnailView.isHidden = false - pdfView.backgroundColor = NCBrandColor.shared.systemBackground + if navigationController?.isNavigationBarHidden ?? false { + navigationController?.setNavigationBarHidden(false, animated: true) + pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor) } else { + navigationController?.setNavigationBarHidden(true, animated: true) + pdfThumbnailScrollViewTopAnchor = pdfThumbnailScrollView.topAnchor.constraint(equalTo: view.topAnchor) + } + + pdfThumbnailScrollViewTopAnchor?.isActive = true + + handlePageChange() + } - let point = recognizer.location(in: pdfView) - if point.y > pdfView.frame.height - thumbnailViewHeight { return } + @objc func gestureOpenPdfThumbnail(_ recognizer: UIScreenEdgePanGestureRecognizer) { - navigationController?.setNavigationBarHidden(true, animated: false) - pdfThumbnailView.isHidden = true - pdfView.backgroundColor = .black + if UIDevice.current.userInterfaceIdiom == .phone && self.pdfThumbnailScrollView.isHidden { + if let tipView = self.tipView { + tipView.dismiss() + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerPDFThumbnail) + self.tipView = nil + } + self.pdfThumbnailScrollView.isHidden = false + self.pdfThumbnailScrollViewWidthAnchor?.constant = thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0) + UIView.animate(withDuration: animateDuration, animations: { + self.pdfThumbnailScrollViewTrailingAnchor?.constant = 0 + self.view.layoutIfNeeded() + }) } + } - handlePageChange() + @objc func gestureClosePdfThumbnail(_ recognizer: UIScreenEdgePanGestureRecognizer) { + + if recognizer.state == .recognized && UIDevice.current.userInterfaceIdiom == .phone && !self.pdfThumbnailScrollView.isHidden { + UIView.animate(withDuration: animateDuration) { + self.pdfThumbnailScrollViewTrailingAnchor?.constant = self.thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0) + self.view.layoutIfNeeded() + } completion: { _ in + self.pdfThumbnailScrollView.isHidden = true + } + } } // MARK: - + func setConstraints() { + + let widthThumbnail = thumbnailViewWidth + (UIApplication.shared.keyWindow?.safeAreaInsets.right ?? 0) + + UIView.animate(withDuration: animateDuration, animations: { + if UIDevice.current.userInterfaceIdiom == .phone { + // Close + self.pdfThumbnailScrollView.isHidden = true + self.pdfThumbnailScrollViewTrailingAnchor?.constant = widthThumbnail + self.pdfThumbnailScrollViewWidthAnchor?.constant = widthThumbnail + } else { + // Open + self.pdfThumbnailScrollViewTrailingAnchor?.constant = 0 + self.pdfThumbnailScrollViewWidthAnchor?.constant = widthThumbnail + } + self.view.layoutIfNeeded() + self.pdfView.autoScales = true + }) + } + @objc func handlePageChange() { guard let curPage = pdfView.currentPage?.pageRef?.pageNumber else { pageView.alpha = 0; return } guard let totalPages = pdfView.document?.pageCount else { return } + let visibleRect = CGRect(x: pdfThumbnailScrollView.contentOffset.x, y: pdfThumbnailScrollView.contentOffset.y, width: pdfThumbnailScrollView.bounds.size.width, height: pdfThumbnailScrollView.bounds.size.height) + let centerPoint = CGPoint(x: visibleRect.size.width/2, y: visibleRect.size.height/2) + let currentPageY = CGFloat(curPage) * thumbnailViewHeight + CGFloat(curPage) * thumbnailPadding + var gotoY = currentPageY - centerPoint.y + + let startY = visibleRect.origin.y < 0 ? 0 : (visibleRect.origin.y + thumbnailViewHeight) + let endY = visibleRect.origin.y + visibleRect.height + + if currentPageY < startY { + if gotoY < 0 { gotoY = 0 } + pdfThumbnailScrollView.setContentOffset(CGPoint(x: 0, y: gotoY), animated: true) + } else if currentPageY > endY { + if gotoY > pdfThumbnailView.frame.height - visibleRect.height { + gotoY = pdfThumbnailView.frame.height - visibleRect.height + } + pdfThumbnailScrollView.setContentOffset(CGPoint(x: 0, y: gotoY), animated: true) + } else { + print("visible") + } + pageView.alpha = 1 pageViewLabel.text = String(curPage) + " " + NSLocalizedString("_of_", comment: "") + " " + String(totalPages) pageViewWidthAnchor?.constant = pageViewLabel.intrinsicContentSize.width + 10 - UIView.animate(withDuration: 1.0, delay: 3.0, animations: { + UIView.animate(withDuration: 1.0, delay: 2.5, animations: { self.pageView.alpha = 0 }) } func searchPdfSelection(_ pdfSelection: PDFSelection) { - pdfSelection.color = .yellow - pdfView.currentSelection = pdfSelection - pdfView.go(to: pdfSelection) + removeAllAnnotations() + + pdfSelection.pages.forEach { page in + let highlight = PDFAnnotation(bounds: pdfSelection.bounds(for: page), forType: .highlight, withProperties: nil) + highlight.endLineStyle = .square + highlight.color = .yellow + page.addAnnotation(highlight) + } + if let page = pdfSelection.pages.first { + pdfView.go(to: page) + } + handlePageChange() } private func selectPage(with label: String) { guard let pdf = pdfView.document else { return } - if let pageNr = Int(label) { - if pageNr > 0 && pageNr <= pdf.pageCount { - if let page = pdf.page(at: pageNr - 1) { - self.pdfView.go(to: page) - } - } else { - let alertController = UIAlertController(title: NSLocalizedString("_invalid_page_", comment: ""), - message: NSLocalizedString("_the_entered_page_number_doesn't_exist_", comment: ""), - preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: nil)) - self.present(alertController, animated: true, completion: nil) - } - } - } + if let pageNr = Int(label) { + if pageNr > 0 && pageNr <= pdf.pageCount { + if let page = pdf.page(at: pageNr - 1) { + self.pdfView.go(to: page) + } + } else { + let alertController = UIAlertController(title: NSLocalizedString("_invalid_page_", comment: ""), + message: NSLocalizedString("_the_entered_page_number_doesn't_exist_", comment: ""), + preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: nil)) + self.present(alertController, animated: true, completion: nil) + } + } + } + + func removeAllAnnotations() { + + guard let document = pdfDocument else { return } + + for i in 0..<document.pageCount { + if let page = document.page(at: i) { + let annotations = page.annotations + for annotation in annotations { + page.removeAnnotation(annotation) + } + } + } + } +} + +extension NCViewerPDF: UIGestureRecognizerDelegate { + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} + +extension NCViewerPDF: EasyTipViewDelegate { + + func easyTipViewDidTap(_ tipView: EasyTipView) { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerPDFThumbnail) + } + + func easyTipViewDidDismiss(_ tipView: EasyTipView) { } } diff --git a/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift b/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift index d86c840c9..cda6cbf65 100644 --- a/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift +++ b/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift @@ -81,7 +81,8 @@ class NCViewerPDFSearch: UITableViewController, UISearchBarDelegate, PDFDocument // } let pdfPage = pdfSelection.pages.first - cell.pageNumberLabel.text = NSLocalizedString("_scan_document_pdf_page_", comment: "") + ": " + (pdfPage?.label ?? "") + let pageNumber = pdfPage?.pageRef?.pageNumber ?? 0 + cell.pageNumberLabel.text = NSLocalizedString("_scan_document_pdf_page_", comment: "") + ": " + String(pageNumber) let extendSelection = pdfSelection.copy() as! PDFSelection extendSelection.extend(atStart: 10) diff --git a/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift b/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift index 7a96388d9..cff53169d 100644 --- a/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift +++ b/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift @@ -4,8 +4,10 @@ // // Created by Marino Faggiana on 03/05/2020. // Copyright © 2020 Marino Faggiana. All rights reserved. +// Copyright © 2022 Henrik Storch. All rights reserved. // // Author Marino Faggiana <marino.faggiana@nextcloud.com> +// Author Henrik Storch <henrik.storch@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 @@ -27,26 +29,24 @@ import NCCommunication @objc class NCViewerQuickLook: QLPreviewController { - let appDelegate = UIApplication.shared.delegate as! AppDelegate - var url: URL? + let url: URL var previewItems: [PreviewItem] = [] - var editingMode: Bool - enum saveModeType { - case overwrite - case copy - case discard - } - var saveMode: saveModeType = .discard + var isEditingEnabled: Bool var metadata: tableMetadata? + // if the document has any changes (annotations) + var hasChanges = false + + // used to display the save alert + var parentVC: UIViewController? + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - @objc init(with url: URL, editingMode: Bool, metadata: tableMetadata?) { - + @objc init(with url: URL, isEditingEnabled: Bool, metadata: tableMetadata?) { self.url = url - self.editingMode = editingMode + self.isEditingEnabled = isEditingEnabled if let metadata = metadata { self.metadata = tableMetadata.init(value: metadata) } @@ -60,72 +60,50 @@ import NCCommunication self.dataSource = self self.delegate = self self.currentPreviewItemIndex = 0 - - self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissPreviewController)) } override func viewDidLoad() { super.viewDidLoad() - - if editingMode && metadata?.classFile == NCCommunicationCommon.typeClassFile.image.rawValue { - Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { t in - if self.navigationItem.rightBarButtonItems?.count ?? 0 > 1 || !(self.navigationController?.isToolbarHidden ?? false) { - if #available(iOS 14.0, *) { - if self.navigationItem.rightBarButtonItems?.count ?? 0 > 1 { - if let buttonItem = self.navigationItem.rightBarButtonItems?.last { - _ = buttonItem.target?.perform(buttonItem.action, with: buttonItem) - } - } else { - if let buttonItem = self.navigationItem.rightBarButtonItems?.first { - _ = buttonItem.target?.perform(buttonItem.action, with: buttonItem) - } - } - } else { - if let buttonItem = self.navigationItem.rightBarButtonItems?.filter({$0.customView != nil}).first?.customView as? UIButton { - buttonItem.sendActions(for: .touchUpInside) - } - } - t.invalidate() - } - }) - } - - if editingMode && metadata?.livePhoto == true { - NCContentPresenter.shared.messageNotification("", description: "_message_disable_overwrite_livephoto_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCharactersForbidden) + guard isEditingEnabled else { return } + + if metadata?.livePhoto == true { + NCContentPresenter.shared.messageNotification( + "", description: "_message_disable_overwrite_livephoto_", + delay: NCGlobal.shared.dismissAfterSecond, + type: NCContentPresenter.messageType.info, + errorCode: NCGlobal.shared.errorCharactersForbidden) } } - @objc func dismissPreviewController() { - - if editingMode { - - let alertController = UIAlertController(title: NSLocalizedString("_save_", comment: ""), message: "", preferredStyle: .alert) - - if metadata?.livePhoto == false { - alertController.addAction(UIAlertAction(title: NSLocalizedString("_overwrite_original_", comment: ""), style: .default) { (_: UIAlertAction) in - self.saveMode = .overwrite - self.dismiss(animated: true) - }) - } - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_save_as_copy_", comment: ""), style: .default) { (_: UIAlertAction) in - self.saveMode = .copy - self.dismiss(animated: true) - }) - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_discard_changes_", comment: ""), style: .destructive) { (_: UIAlertAction) in - self.saveMode = .discard - self.dismiss(animated: true) - }) + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // needs to be saved bc in didDisappear presentingVC is already nil + self.parentVC = presentingViewController + } - alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in }) + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) - self.present(alertController, animated: true) + guard isEditingEnabled, hasChanges, let metadata = metadata else { return } + let alertController = UIAlertController(title: NSLocalizedString("_save_", comment: ""), message: nil, preferredStyle: .alert) + var message: String? + if metadata.livePhoto { + message = NSLocalizedString("_message_disable_overwrite_livephoto_", comment: "") + } else if metadata.lock { + message = NSLocalizedString("_file_locked_no_override_", comment: "") } else { - - self.dismiss(animated: true) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_overwrite_original_", comment: ""), style: .default) { _ in + self.saveModifiedFile(override: true) + }) } + alertController.message = message + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_save_as_copy_", comment: ""), style: .default) { _ in + self.saveModifiedFile(override: false) + }) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_discard_changes_", comment: ""), style: .destructive) { _ in }) + parentVC?.present(alertController, animated: true) } } @@ -139,46 +117,51 @@ extension NCViewerQuickLook: QLPreviewControllerDataSource, QLPreviewControllerD previewItems[index] } - func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: QLPreviewItem) { - } - @available(iOS 13.0, *) func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode { - if editingMode { - return .createCopy - } else { - return .disabled - } + return isEditingEnabled ? .createCopy : .disabled } - func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) { - - if saveMode != .discard { - - guard let metadata = self.metadata else { return } - let ocId = NSUUID().uuidString - let size = NCUtilityFileSystem.shared.getFileSize(filePath: modifiedContentsURL.path) - - if saveMode == .copy { - let fileName = NCUtilityFileSystem.shared.createFileName(metadata.fileNameView, serverUrl: metadata.serverUrl, account: metadata.account) - metadata.fileName = fileName - metadata.fileNameView = fileName - } - - let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: metadata.fileNameView)! + fileprivate func saveModifiedFile(override: Bool) { + guard let metadata = self.metadata else { return } - if NCUtilityFileSystem.shared.copyFile(atPath: modifiedContentsURL.path, toPath: fileNamePath) { + let ocId = NSUUID().uuidString + let size = NCUtilityFileSystem.shared.getFileSize(filePath: url.path) - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: metadata.account, user: metadata.user, userId: metadata.userId, fileName: metadata.fileName, fileNameView: metadata.fileNameView, ocId: ocId, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, url: modifiedContentsURL.path, contentType: "", livePhoto: false) + if !override { + let fileName = NCUtilityFileSystem.shared.createFileName(metadata.fileNameView, serverUrl: metadata.serverUrl, account: metadata.account) + metadata.fileName = fileName + metadata.fileNameView = fileName + } - metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground - metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile - metadataForUpload.size = size - metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload + guard let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: metadata.fileNameView), + NCUtilityFileSystem.shared.copyFile(atPath: url.path, toPath: fileNamePath) else { return } + + let metadataForUpload = NCManageDatabase.shared.createMetadata( + account: metadata.account, + user: metadata.user, + userId: metadata.userId, + fileName: metadata.fileName, + fileNameView: metadata.fileNameView, + ocId: ocId, + serverUrl: metadata.serverUrl, + urlBase: metadata.urlBase, + url: url.path, + contentType: "", + livePhoto: false) + + metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground + metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile + metadataForUpload.size = size + metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload + (UIApplication.shared.delegate as? AppDelegate)?.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload]) + } - appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload]) - } - } + func previewController(_ controller: QLPreviewController, didSaveEditedCopyOf previewItem: QLPreviewItem, at modifiedContentsURL: URL) { + // easier to handle that way than to use `.updateContents` + // needs to be moved otherwise it will only be called once! + guard NCUtilityFileSystem.shared.moveFile(atPath: modifiedContentsURL.path, toPath: url.path) else { return } + hasChanges = true } } |