diff --git a/CHANGELOG.md b/CHANGELOG.md index 03058aba8..d69a8e65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,73 @@ +Changelog for ownCloud iOS Client [12.0.3] (2023-08-31) +======================================= +The following sections list the changes in ownCloud iOS Client 12.0.3 relevant to +ownCloud admins and users. + +[12.0.3]: https://github.com/owncloud/ios-app/compare/milestone/12.0.2...milestone/12.0.3 + +Summary +------- + +* Bugfix - Upload-Metadata: [#1227](https://github.com/owncloud/ios-app/issues/1227) +* Bugfix - Open Folder in Files.app: [#1240](https://github.com/owncloud/ios-app/issues/1240) +* Bugfix - Connection name: [#1254](https://github.com/owncloud/ios-app/issues/1254) +* Bugfix - Unable to access files from Files.app: [#1262](https://github.com/owncloud/ios-app/issues/1262) +* Bugfix - File Provider Crash: [#1266](https://github.com/owncloud/ios-app/issues/1266) +* Bugfix - Translation: [#1269](https://github.com/owncloud/ios-app/pull/1269) +* Bugfix - Open in Web for ownCloud 10: [#5747](https://github.com/owncloud/enterprise/issues/5747) +* Bugfix - Copy Item not working: [#5889](https://github.com/owncloud/enterprise/issues/5889) + +Details +------- + +* Bugfix - Upload-Metadata: [#1227](https://github.com/owncloud/ios-app/issues/1227) + + TUS -H Upload-Metadata: mtime missing + + https://github.com/owncloud/ios-app/issues/1227 + +* Bugfix - Open Folder in Files.app: [#1240](https://github.com/owncloud/ios-app/issues/1240) + + In some cases it was not possible to open Folder using iOS Files.app. + + https://github.com/owncloud/ios-app/issues/1240 + +* Bugfix - Connection name: [#1254](https://github.com/owncloud/ios-app/issues/1254) + + Connection name doesn't change immediately + + https://github.com/owncloud/ios-app/issues/1254 + +* Bugfix - Unable to access files from Files.app: [#1262](https://github.com/owncloud/ios-app/issues/1262) + + In some cases it was not possible to access files from the Files.app. + + https://github.com/owncloud/ios-app/issues/1262 + +* Bugfix - File Provider Crash: [#1266](https://github.com/owncloud/ios-app/issues/1266) + + IOS invokes a FileProvider method with a nil value, which causes a crash. + + https://github.com/owncloud/ios-app/issues/1266 + +* Bugfix - Translation: [#1269](https://github.com/owncloud/ios-app/pull/1269) + + Updated translations from Transifex. + + https://github.com/owncloud/ios-app/pull/1269 + +* Bugfix - Open in Web for ownCloud 10: [#5747](https://github.com/owncloud/enterprise/issues/5747) + + Open in Web feature was not available for ownCloud 10 instances in the app. + + https://github.com/owncloud/enterprise/issues/5747 + +* Bugfix - Copy Item not working: [#5889](https://github.com/owncloud/enterprise/issues/5889) + + Copy and paste to a Space was not working + + https://github.com/owncloud/enterprise/issues/5889 + Changelog for ownCloud iOS Client [12.0.2] (2023-06-23) ======================================= The following sections list the changes in ownCloud iOS Client 12.0.2 relevant to diff --git a/changelog/12.0.3_2023-08-31/1227 b/changelog/12.0.3_2023-08-31/1227 new file mode 100644 index 000000000..b17c660f8 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1227 @@ -0,0 +1,5 @@ +Bugfix: Upload-Metadata + +TUS -H Upload-Metadata: mtime missing + +https://github.com/owncloud/ios-app/issues/1227 diff --git a/changelog/12.0.3_2023-08-31/1240 b/changelog/12.0.3_2023-08-31/1240 new file mode 100644 index 000000000..6bdce670c --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1240 @@ -0,0 +1,5 @@ +Bugfix: Open Folder in Files.app + +In some cases it was not possible to open Folder using iOS Files.app. + +https://github.com/owncloud/ios-app/issues/1240 diff --git a/changelog/12.0.3_2023-08-31/1254 b/changelog/12.0.3_2023-08-31/1254 new file mode 100644 index 000000000..6cec6fc63 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1254 @@ -0,0 +1,5 @@ +Bugfix: Connection name + +Connection name doesn't change immediately + +https://github.com/owncloud/ios-app/issues/1254 diff --git a/changelog/12.0.3_2023-08-31/1262 b/changelog/12.0.3_2023-08-31/1262 new file mode 100644 index 000000000..c8f199d69 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1262 @@ -0,0 +1,5 @@ +Bugfix: Unable to access files from Files.app + +In some cases it was not possible to access files from the Files.app. + +https://github.com/owncloud/ios-app/issues/1262 diff --git a/changelog/12.0.3_2023-08-31/1266 b/changelog/12.0.3_2023-08-31/1266 new file mode 100644 index 000000000..73e0ef041 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1266 @@ -0,0 +1,5 @@ +Bugfix: File Provider Crash + +iOS invokes a FileProvider method with a nil value, which causes a crash. + +https://github.com/owncloud/ios-app/issues/1266 diff --git a/changelog/12.0.3_2023-08-31/1269 b/changelog/12.0.3_2023-08-31/1269 new file mode 100644 index 000000000..6a73e73b6 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/1269 @@ -0,0 +1,5 @@ +Bugfix: Translation + +Updated translations from Transifex. + +https://github.com/owncloud/ios-app/pull/1269 diff --git a/changelog/12.0.3_2023-08-31/5747 b/changelog/12.0.3_2023-08-31/5747 new file mode 100644 index 000000000..a5c7d6843 --- /dev/null +++ b/changelog/12.0.3_2023-08-31/5747 @@ -0,0 +1,5 @@ +Bugfix: Open in Web for ownCloud 10 + +Open in Web feature was not available for ownCloud 10 instances in the app. + +https://github.com/owncloud/enterprise/issues/5747 diff --git a/changelog/12.0.3_2023-08-31/5889 b/changelog/12.0.3_2023-08-31/5889 new file mode 100644 index 000000000..1573f1e9c --- /dev/null +++ b/changelog/12.0.3_2023-08-31/5889 @@ -0,0 +1,5 @@ +Bugfix: Copy Item not working + +Copy and paste to a Space was not working + +https://github.com/owncloud/enterprise/issues/5889 diff --git a/doc/CONFIGURATION.json b/doc/CONFIGURATION.json index c1ab25081..643b0efb2 100644 --- a/doc/CONFIGURATION.json +++ b/doc/CONFIGURATION.json @@ -52,6 +52,10 @@ "key" : "allowed", "label" : "action.allowed", "possibleValues" : [ + { + "description" : "Make available offline", + "value" : "com.owncloud.action.availableOffline" + }, { "description" : "Sharing", "value" : "com.owncloud.action.collaborate" @@ -88,14 +92,6 @@ "description" : "Paste", "value" : "com.owncloud.action.importpasteboard" }, - { - "description" : "Make available offline", - "value" : "com.owncloud.action.makeAvailableOffline" - }, - { - "description" : "Available Offline", - "value" : "com.owncloud.action.makeUnavailableOffline" - }, { "description" : "Markup", "value" : "com.owncloud.action.markup" @@ -190,6 +186,10 @@ "key" : "disallowed", "label" : "action.disallowed", "possibleValues" : [ + { + "description" : "Make available offline", + "value" : "com.owncloud.action.availableOffline" + }, { "description" : "Sharing", "value" : "com.owncloud.action.collaborate" @@ -226,14 +226,6 @@ "description" : "Paste", "value" : "com.owncloud.action.importpasteboard" }, - { - "description" : "Make available offline", - "value" : "com.owncloud.action.makeAvailableOffline" - }, - { - "description" : "Available Offline", - "value" : "com.owncloud.action.makeUnavailableOffline" - }, { "description" : "Markup", "value" : "com.owncloud.action.markup" @@ -1667,6 +1659,10 @@ "description" : "Extension with the identifier auth-race-condition.", "value" : "auth-race-condition" }, + { + "description" : "Extension with the identifier com.owncloud.action.availableOffline.", + "value" : "com.owncloud.action.availableOffline" + }, { "description" : "Extension with the identifier com.owncloud.action.background_update.", "value" : "com.owncloud.action.background_update" @@ -1711,14 +1707,6 @@ "description" : "Extension with the identifier com.owncloud.action.instant_media_upload.", "value" : "com.owncloud.action.instant_media_upload" }, - { - "description" : "Extension with the identifier com.owncloud.action.makeAvailableOffline.", - "value" : "com.owncloud.action.makeAvailableOffline" - }, - { - "description" : "Extension with the identifier com.owncloud.action.makeUnavailableOffline.", - "value" : "com.owncloud.action.makeUnavailableOffline" - }, { "description" : "Extension with the identifier com.owncloud.action.markup.", "value" : "com.owncloud.action.markup" diff --git a/doc/configuration.adoc b/doc/configuration.adoc index 1b258b214..e9d0e18d2 100644 --- a/doc/configuration.adoc +++ b/doc/configuration.adoc @@ -17,6 +17,9 @@ tag::actions[] !=== ! Value ! Description +! `com.owncloud.action.availableOffline` +! Make available offline + ! `com.owncloud.action.collaborate` ! Sharing @@ -44,12 +47,6 @@ tag::actions[] ! `com.owncloud.action.importpasteboard` ! Paste -! `com.owncloud.action.makeAvailableOffline` -! Make available offline - -! `com.owncloud.action.makeUnavailableOffline` -! Available Offline - ! `com.owncloud.action.markup` ! Markup @@ -121,6 +118,9 @@ action.create-document-mode !=== ! Value ! Description +! `com.owncloud.action.availableOffline` +! Make available offline + ! `com.owncloud.action.collaborate` ! Sharing @@ -148,12 +148,6 @@ action.create-document-mode ! `com.owncloud.action.importpasteboard` ! Paste -! `com.owncloud.action.makeAvailableOffline` -! Make available offline - -! `com.owncloud.action.makeUnavailableOffline` -! Available Offline - ! `com.owncloud.action.markup` ! Markup @@ -1008,6 +1002,9 @@ tag::extensions[] ! `auth-race-condition` ! Extension with the identifier auth-race-condition. +! `com.owncloud.action.availableOffline` +! Extension with the identifier com.owncloud.action.availableOffline. + ! `com.owncloud.action.background_update` ! Extension with the identifier com.owncloud.action.background_update. @@ -1041,12 +1038,6 @@ tag::extensions[] ! `com.owncloud.action.instant_media_upload` ! Extension with the identifier com.owncloud.action.instant_media_upload. -! `com.owncloud.action.makeAvailableOffline` -! Extension with the identifier com.owncloud.action.makeAvailableOffline. - -! `com.owncloud.action.makeUnavailableOffline` -! Extension with the identifier com.owncloud.action.makeUnavailableOffline. - ! `com.owncloud.action.markup` ! Extension with the identifier com.owncloud.action.markup. diff --git a/fastlane/metadata-emm/en-US/release_notes.txt b/fastlane/metadata-emm/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata-emm/en-US/release_notes.txt +++ b/fastlane/metadata-emm/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/fastlane/metadata-owncloud-online/en-US/release_notes.txt b/fastlane/metadata-owncloud-online/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata-owncloud-online/en-US/release_notes.txt +++ b/fastlane/metadata-owncloud-online/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt index 9633be580..e9ca129c0 100644 --- a/fastlane/metadata/en-US/release_notes.txt +++ b/fastlane/metadata/en-US/release_notes.txt @@ -1,7 +1,3 @@ -Bug Fixes - -• Added missing Recents Group -• Unable to authenticate using OpenID Connect -• Attach files from third-party apps -• Solves "Content unavailable" in Files.app +• Bug Fixes +Fixed File Provider, App Provider and layout issues. diff --git a/ownCloud.xcodeproj/project.pbxproj b/ownCloud.xcodeproj/project.pbxproj index e2ca171c0..df5f36e30 100644 --- a/ownCloud.xcodeproj/project.pbxproj +++ b/ownCloud.xcodeproj/project.pbxproj @@ -273,8 +273,6 @@ DC2A68D529D492B300BFF393 /* space.tvg in Resources */ = {isa = PBXBuildFile; fileRef = DC2A68D429D492B200BFF393 /* space.tvg */; }; DC2A68D729D4E93300BFF393 /* SharedKeyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2A68D629D4E93300BFF393 /* SharedKeyCommands.swift */; }; DC2FE2DA24C30586002AFDB3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 593A821320C7D4C5000E2A90 /* Localizable.strings */; }; - DC33939622E0747400DD3DA4 /* MakeAvailableOfflineAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC33939522E0747400DD3DA4 /* MakeAvailableOfflineAction.swift */; }; - DC33939D22E076E300DD3DA4 /* MakeUnavailableOfflineAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC33939C22E076E300DD3DA4 /* MakeUnavailableOfflineAction.swift */; }; DC36885824DC98BF00333600 /* OCFileProviderServiceSession.h in Headers */ = {isa = PBXBuildFile; fileRef = DC36885624DC98BF00333600 /* OCFileProviderServiceSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; DC36885924DC98BF00333600 /* OCFileProviderServiceSession.m in Sources */ = {isa = PBXBuildFile; fileRef = DC36885724DC98BF00333600 /* OCFileProviderServiceSession.m */; }; DC36885D24DD916800333600 /* ownCloudApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC0855C2293F1FD008CC05C /* ownCloudApp.framework */; }; @@ -370,6 +368,7 @@ DC7C101124B5FA7700227085 /* OCBookmark+AppExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7C100E24B5F81E00227085 /* OCBookmark+AppExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DC7C101224B5FD6500227085 /* OCBookmark+AppExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7C100F24B5F81E00227085 /* OCBookmark+AppExtensions.m */; }; DC7DBA37207F84BF00E7337D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7DBA36207F84BF00E7337D /* main.swift */; }; + DC815C3F2A65D9CB00BFF393 /* AvailableOfflineAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC815C3E2A65D9CB00BFF393 /* AvailableOfflineAction.swift */; }; DC825E352A05083C00BFF393 /* GitInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC825E342A05083C00BFF393 /* GitInfo.swift */; }; DC82663C28168D2800F91F7D /* ClientContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82663B28168D2800F91F7D /* ClientContext.swift */; }; DC82D6FA23171339001551C5 /* ScanAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82D6F923171339001551C5 /* ScanAction.swift */; }; @@ -1287,8 +1286,6 @@ DC2A68D429D492B200BFF393 /* space.tvg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = space.tvg; path = "img/filetypes-tvg/space.tvg"; sourceTree = SOURCE_ROOT; }; DC2A68D629D4E93300BFF393 /* SharedKeyCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedKeyCommands.swift; sourceTree = ""; }; DC321260207EB01B00DB171D /* ThemeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeImage.swift; sourceTree = ""; }; - DC33939522E0747400DD3DA4 /* MakeAvailableOfflineAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeAvailableOfflineAction.swift; sourceTree = ""; }; - DC33939C22E076E300DD3DA4 /* MakeUnavailableOfflineAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MakeUnavailableOfflineAction.swift; sourceTree = ""; }; DC3393A722E0C4ED00DD3DA4 /* icon-available-offline.tvg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "icon-available-offline.tvg"; path = "../img/filetypes-tvg/icon-available-offline.tvg"; sourceTree = ""; }; DC36885624DC98BF00333600 /* OCFileProviderServiceSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCFileProviderServiceSession.h; sourceTree = ""; }; DC36885724DC98BF00333600 /* OCFileProviderServiceSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCFileProviderServiceSession.m; sourceTree = ""; }; @@ -1399,6 +1396,7 @@ DC7DBA36207F84BF00E7337D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; DC7DBA52207F8BD600E7337D /* img */ = {isa = PBXFileReference; lastKnownFileType = folder; path = img; sourceTree = SOURCE_ROOT; }; DC7DBA53207FA80C00E7337D /* TVGImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVGImage.swift; sourceTree = ""; }; + DC815C3E2A65D9CB00BFF393 /* AvailableOfflineAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableOfflineAction.swift; sourceTree = ""; }; DC825E342A05083C00BFF393 /* GitInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitInfo.swift; sourceTree = ""; }; DC82663B28168D2800F91F7D /* ClientContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientContext.swift; sourceTree = ""; }; DC82D6F923171339001551C5 /* ScanAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanAction.swift; sourceTree = ""; }; @@ -2185,8 +2183,7 @@ 6E91F37D21ECA6FD009436D2 /* CopyAction.swift */, 6E586CFF2199A78E00F680C4 /* DeleteAction.swift */, 6E3A104C219D6F0100F90C96 /* DuplicateAction.swift */, - DC33939522E0747400DD3DA4 /* MakeAvailableOfflineAction.swift */, - DC33939C22E076E300DD3DA4 /* MakeUnavailableOfflineAction.swift */, + DC815C3E2A65D9CB00BFF393 /* AvailableOfflineAction.swift */, 6E586CFD2199A75900F680C4 /* MoveAction.swift */, 6E586CFB2199A72600F680C4 /* OpenInAction.swift */, 6E3A103D219D5BBA00F90C96 /* RenameAction.swift */, @@ -4373,7 +4370,6 @@ 4C7295D8228C384E00FA4E68 /* LogFilesViewController.swift in Sources */, DC01CDCC212EDDF600FC8E38 /* TextViewController.swift in Sources */, 4CB8ADE622DF6C2B00F1FEBC /* CIImage+Extensions.swift in Sources */, - DC33939D22E076E300DD3DA4 /* MakeUnavailableOfflineAction.swift in Sources */, 3998F5D72241486F00B66713 /* OCCertificate+Extension.swift in Sources */, 6E4F1734217749910049A71B /* ImageDisplayViewController.swift in Sources */, DC8EB271239308E5009148F9 /* LicenseOffersViewController.swift in Sources */, @@ -4403,6 +4399,7 @@ 4C464BF02187AF1500D30602 /* PDFTocTableViewController.swift in Sources */, 3961281622F8730A0087BD3A /* SceneDelegate.swift in Sources */, DCA35D8124D1707100DBE2B0 /* OCSyncRecordActivity+DiagnosticGenerator.swift in Sources */, + DC815C3F2A65D9CB00BFF393 /* AvailableOfflineAction.swift in Sources */, 4CB8ADE022DF5EC500F1FEBC /* UIAlertViewController+SystemPermissions.swift in Sources */, 392DDB1424CF024D009E5406 /* ImportFilesController.swift in Sources */, 396C82FB2319AFDD00938262 /* CollaborateAction.swift in Sources */, @@ -4501,7 +4498,6 @@ DC2323E62AA865A700BFF393 /* BookmarkSetupStepEnterURLViewController.swift in Sources */, 23EC77592137F3DD0032D4E6 /* DisplayExtension.swift in Sources */, DCDF58B323CE82E100080BEB /* LicenseInAppPurchaseFeatureView.swift in Sources */, - DC33939622E0747400DD3DA4 /* MakeAvailableOfflineAction.swift in Sources */, 02D4C82A255208E60000E299 /* PDFSearchResultsView.swift in Sources */, 39057AA3233BA7A60008E6C0 /* Intents.intentdefinition in Sources */, 6E586CFE2199A75900F680C4 /* MoveAction.swift in Sources */, diff --git a/ownCloud/App Controllers/AppRootViewController.swift b/ownCloud/App Controllers/AppRootViewController.swift index 9852019cb..efb5aaa47 100644 --- a/ownCloud/App Controllers/AppRootViewController.swift +++ b/ownCloud/App Controllers/AppRootViewController.swift @@ -124,7 +124,6 @@ open class AppRootViewController: EmbeddingViewController, BrowserNavigationView let configuration = BookmarkComposerConfiguration.newBookmarkConfiguration configuration.hasIntro = true self?.contentViewController = BookmarkSetupViewController(configuration: configuration) - // self?.contentViewController = InitialSetupViewController() } else { // Account already available self?.contentViewController = self?.contentBrowserController diff --git a/ownCloud/AppDelegate.swift b/ownCloud/AppDelegate.swift index a0df985a1..477b64a1b 100644 --- a/ownCloud/AppDelegate.swift +++ b/ownCloud/AppDelegate.swift @@ -78,8 +78,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { OCExtensionManager.shared.addExtension(UploadMediaAction.actionExtension) OCExtensionManager.shared.addExtension(UploadCameraMediaAction.actionExtension) OCExtensionManager.shared.addExtension(UnshareAction.actionExtension) - OCExtensionManager.shared.addExtension(MakeAvailableOfflineAction.actionExtension) - OCExtensionManager.shared.addExtension(MakeUnavailableOfflineAction.actionExtension) + OCExtensionManager.shared.addExtension(AvailableOfflineAction.actionExtension) OCExtensionManager.shared.addExtension(CollaborateAction.actionExtension) OCExtensionManager.shared.addExtension(FavoriteAction.actionExtension) OCExtensionManager.shared.addExtension(UnfavoriteAction.actionExtension) diff --git a/ownCloud/Client/Actions/Actions+Extensions/AvailableOfflineAction.swift b/ownCloud/Client/Actions/Actions+Extensions/AvailableOfflineAction.swift new file mode 100644 index 000000000..05534775f --- /dev/null +++ b/ownCloud/Client/Actions/Actions+Extensions/AvailableOfflineAction.swift @@ -0,0 +1,129 @@ +// +// AvailableOfflineAction.swift +// ownCloud +// +// Created by Felix Schwarz on 17.07.23. +// Copyright © 2023 ownCloud GmbH. All rights reserved. +// + +/* + * Copyright (C) 2023, ownCloud GmbH. + * + * This code is covered by the GNU Public License Version 3. + * + * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ + * You should have received a copy of this license along with this program. If not, see . + * + */ + +import UIKit +import ownCloudSDK +import ownCloudAppShared + +class AvailableOfflineAction: Action { + override class var identifier : OCExtensionIdentifier? { return OCExtensionIdentifier("com.owncloud.action.availableOffline") } + override class var category : ActionCategory? { return .normal } + override class var name : String? { return "Make available offline".localized } + override class var locations : [OCExtensionLocationIdentifier]? { return [.moreItem, .moreDetailItem, .moreFolder, .keyboardShortcut, .contextMenuItem] } + override class var keyCommand : String? { return "O" } + override class var keyModifierFlags: UIKeyModifierFlags? { return [.command, .alternate] } + + // MARK: - Extension matching + override class func applicablePosition(forContext context: ActionContext) -> ActionPosition { + guard context.items.count > 0, let core = context.core else { + return .none + } + + for item in context.items { + if let itemLocation = item.location, let policies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { + for policy in policies { + // Only show if item is not already available offline via parent item + if let policyLocation = policy.location, itemLocation.isLocated(in: policyLocation) && itemLocation != policyLocation { + return .none + } + } + } + } + + return .middle + } + + var isAvailableOffline: Bool { + get { + var availableOfflineCount: Int = 0 + + if let core { + for item in context.items { + if let policies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { + if policies.count > 0 { + availableOfflineCount += 1 + } + } + } + } + + return (availableOfflineCount == context.items.count) + } + + set { + if let core { + if newValue { + // Make available offline + for item in context.items { + core.makeAvailableOffline(item, options: [.skipRedundancyChecks : true, .convertExistingLocalDownloads : true], completionHandler: nil) + } + } else { + // Make unavailable offline + for item in context.items { + if let itemPolicies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { + for itemPolicy in itemPolicies { + if (itemPolicy.location == item.location) || (itemPolicy.localID == item.localID) { + core.removeAvailableOfflinePolicy(itemPolicy, completionHandler: nil) + } + } + } + } + } + } + } + } + + // MARK: - Action implementation + override func run() { + guard context.items.count > 0 else { + completed(with: NSError(ocError: .insufficientParameters)) + return + } + + let newAvailableOffline = !isAvailableOffline + + isAvailableOffline = newAvailableOffline + availableOfflineSwitch?.isOn = isAvailableOffline + + self.completed() + } + + var availableOfflineSwitch: UISwitch? + + override func provideStaticRow() -> StaticTableViewRow? { + if let staticRow = super.provideStaticRow() { + availableOfflineSwitch = UISwitch(frame: .zero, primaryAction: UIAction(handler: { [weak self] action in + if let self, let availableOfflineSwitch = self.availableOfflineSwitch { + self.isAvailableOffline = availableOfflineSwitch.isOn + } + })) + + availableOfflineSwitch?.isOn = isAvailableOffline + + staticRow.cell?.accessoryView = availableOfflineSwitch + + return staticRow + } + return nil + } + + override class func iconForLocation(_ location: OCExtensionLocationIdentifier) -> UIImage? { + return UIImage(named: "available-offline")?.withRenderingMode(.alwaysTemplate) + } + +} diff --git a/ownCloud/Client/Actions/Actions+Extensions/MakeAvailableOfflineAction.swift b/ownCloud/Client/Actions/Actions+Extensions/MakeAvailableOfflineAction.swift deleted file mode 100644 index 62a257d17..000000000 --- a/ownCloud/Client/Actions/Actions+Extensions/MakeAvailableOfflineAction.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// MakeAvailableOfflineAction.swift -// ownCloud -// -// Created by Felix Schwarz on 18.07.19. -// Copyright © 2019 ownCloud GmbH. All rights reserved. -// - -/* - * Copyright (C) 2019, ownCloud GmbH. - * - * This code is covered by the GNU Public License Version 3. - * - * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ - * You should have received a copy of this license along with this program. If not, see . - * - */ - -import UIKit -import ownCloudSDK -import ownCloudAppShared - -class MakeAvailableOfflineAction: Action { - override class var identifier : OCExtensionIdentifier? { return OCExtensionIdentifier("com.owncloud.action.makeAvailableOffline") } - override class var category : ActionCategory? { return .normal } - override class var name : String? { return "Make available offline".localized } - override class var locations : [OCExtensionLocationIdentifier]? { return [.moreItem, .moreDetailItem, .moreFolder, .keyboardShortcut, .contextMenuItem] } - override class var keyCommand : String? { return "O" } - override class var keyModifierFlags: UIKeyModifierFlags? { return [.command, .alternate] } - - // MARK: - Extension matching - override class func applicablePosition(forContext context: ActionContext) -> ActionPosition { - guard context.items.count > 0, let core = context.core else { - return .none - } - - // Only show if item is not already available offline - var position : ActionPosition = .middle - - for item in context.items { - if let policies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { - if policies.count > 0 { - position = .none - break - } - } - } - - return position - } - - // MARK: - Action implementation - override func run() { - guard context.items.count > 0, let core = self.core else { - completed(with: NSError(ocError: .insufficientParameters)) - return - } - - for item in context.items { - core.makeAvailableOffline(item, options: [.skipRedundancyChecks : true, .convertExistingLocalDownloads : true], completionHandler: nil) - } - - self.completed() - } - - override class func iconForLocation(_ location: OCExtensionLocationIdentifier) -> UIImage? { - return UIImage(named: "available-offline")?.withRenderingMode(.alwaysTemplate) - } -} diff --git a/ownCloud/Client/Actions/Actions+Extensions/MakeUnavailableOfflineAction.swift b/ownCloud/Client/Actions/Actions+Extensions/MakeUnavailableOfflineAction.swift deleted file mode 100644 index f0e11a8b8..000000000 --- a/ownCloud/Client/Actions/Actions+Extensions/MakeUnavailableOfflineAction.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// MakeUnavailableOfflineAction.swift -// ownCloud -// -// Created by Felix Schwarz on 18.07.19. -// Copyright © 2019 ownCloud GmbH. All rights reserved. -// - -/* - * Copyright (C) 2019, ownCloud GmbH. - * - * This code is covered by the GNU Public License Version 3. - * - * For distribution utilizing Apple mechanisms please see https://owncloud.org/contribute/iOS-license-exception/ - * You should have received a copy of this license along with this program. If not, see . - * - */ - -import UIKit -import ownCloudSDK -import ownCloudAppShared - -class MakeUnavailableOfflineAction: Action { - override class var identifier : OCExtensionIdentifier? { return OCExtensionIdentifier("com.owncloud.action.makeUnavailableOffline") } - override class var category : ActionCategory? { return .normal } - override class var name : String? { return "Available Offline".localized } - override class var locations : [OCExtensionLocationIdentifier]? { return [.moreItem, .moreDetailItem, .moreFolder, .keyboardShortcut, .contextMenuItem] } - override class var keyCommand : String? { return "O" } - override class var keyModifierFlags: UIKeyModifierFlags? { return [.command, .alternate] } - - // MARK: - Extension matching - override class func applicablePosition(forContext context: ActionContext) -> ActionPosition { - guard context.items.count > 0, let core = context.core else { - return .none - } - - // Only show if item is not already available offline - var position : ActionPosition = .none - - for item in context.items { - if let itemPolicies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { - if itemPolicies.contains(where: { (itemPolicy) -> Bool in - return (itemPolicy.location == item.location) || (itemPolicy.localID == item.localID) - }) { - position = .middle - } - } - } - - return position - } - - // MARK: - Action implementation - override func run() { - guard context.items.count > 0, let core = self.core else { - completed(with: NSError(ocError: .insufficientParameters)) - return - } - - for item in context.items { - if let itemPolicies = core.retrieveAvailableOfflinePoliciesCovering(item, completionHandler: nil) { - for itemPolicy in itemPolicies { - if (itemPolicy.location == item.location) || (itemPolicy.localID == item.localID) { - core.removeAvailableOfflinePolicy(itemPolicy, completionHandler: nil) - } - } - } - } - - self.completed() - } - - override func provideStaticRow() -> StaticTableViewRow? { - // Add checkmark - if let staticRow = super.provideStaticRow() { - staticRow.cell?.accessoryType = .checkmark - return staticRow - } - return nil - } - - override class func iconForLocation(_ location: OCExtensionLocationIdentifier) -> UIImage? { - return UIImage(named: "available-offline")?.withRenderingMode(.alwaysTemplate) - } -} diff --git a/ownCloud/Resources/en.lproj/Localizable.strings b/ownCloud/Resources/en.lproj/Localizable.strings index ee8167459..fdbe1486b 100644 --- a/ownCloud/Resources/en.lproj/Localizable.strings +++ b/ownCloud/Resources/en.lproj/Localizable.strings @@ -104,6 +104,7 @@ "kind" = "kind"; "size" = "size"; "date" = "date"; +"last used" = "last used"; "Tree" = "Tree"; "Search folder" = "Search folder"; diff --git a/ownCloudAppShared/Client/Account/Controller/AccountController.swift b/ownCloudAppShared/Client/Account/Controller/AccountController.swift index cc91b4375..02e3c040d 100644 --- a/ownCloudAppShared/Client/Account/Controller/AccountController.swift +++ b/ownCloudAppShared/Client/Account/Controller/AccountController.swift @@ -407,7 +407,7 @@ public class AccountController: NSObject, OCDataItem, OCDataItemVersioning, Acco // Recents if specialItems[.recents] == nil { - specialItems[.recents] = OCSavedSearch(scope: .account, location: nil, name: "Recents".localized, isTemplate: false, searchTerm: ":recent :file").withCustomIcon(name: "clock.arrow.circlepath").useNameAsTitle(true) + specialItems[.recents] = OCSavedSearch(scope: .account, location: nil, name: "Recents".localized, isTemplate: false, searchTerm: ":recent :file").withCustomIcon(name: "clock.arrow.circlepath").useNameAsTitle(true).useSortDescriptor(SortDescriptor(method: .lastUsed, direction: .ascendant)) } if let sideBarItem = specialItems[.recents] { quickAccessItems.append(sideBarItem) diff --git a/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift b/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift index bb42e5591..570c5944b 100644 --- a/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift +++ b/ownCloudAppShared/Client/Actions/Implementations/OpenInWebAppAction.swift @@ -160,6 +160,10 @@ public class OpenInWebAppAction: Action { } } + if context.items.count == 1, let item = context.items.first, let core = context.core { + core.updateLastUsed(for: item) + } + switch openMode { case .defaultBrowser: openInExternalBrowser() @@ -201,7 +205,7 @@ public class OpenInWebAppAction: Action { // Open in in-app browser core.connection.open(inApp: item, with: app, viewMode: nil, completionHandler: { (error, url, method, headers, parameters, urlRequest) in - if let error = error { + if let error { OnMainThread { let appName = self.app?.name ?? "app" let itemName = item.name ?? "item" diff --git a/ownCloudAppShared/Client/Data Item Interactions/OCSavedSearch+Interactions.swift b/ownCloudAppShared/Client/Data Item Interactions/OCSavedSearch+Interactions.swift index d9c77c263..6e9c233bc 100644 --- a/ownCloudAppShared/Client/Data Item Interactions/OCSavedSearch+Interactions.swift +++ b/ownCloudAppShared/Client/Data Item Interactions/OCSavedSearch+Interactions.swift @@ -23,6 +23,7 @@ import ownCloudApp extension OCSavedSearchUserInfoKey { static let customIconName = OCSavedSearchUserInfoKey(rawValue: "customIconName") static let useNameAsTitle = OCSavedSearchUserInfoKey(rawValue: "useNameAsTitle") + static let useSortDescriptor = OCSavedSearchUserInfoKey(rawValue: "useSortDescriptor") } extension OCSavedSearch { @@ -108,6 +109,20 @@ extension OCSavedSearch { } } + var useSortDescriptor: SortDescriptor? { + set { + if userInfo == nil, let newValue { + userInfo = [.useSortDescriptor : newValue] + } else { + userInfo?[.useSortDescriptor] = newValue + } + } + + get { + return userInfo?[.useSortDescriptor] as? SortDescriptor + } + } + func withCustomIcon(name: String) -> OCSavedSearch { customIconName = name return self @@ -117,16 +132,25 @@ extension OCSavedSearch { useNameAsTitle = useIt return self } + + func useSortDescriptor(_ sortDescriptor: SortDescriptor) -> OCSavedSearch { + useSortDescriptor = sortDescriptor + return self + } } extension OCSavedSearch: DataItemSelectionInteraction { func buildViewController(with context: ClientContext) -> ClientItemViewController? { if let condition = condition() { let query = OCQuery(condition: condition, inputFilter: nil) + let useSortDescriptor = useSortDescriptor DisplaySettings.shared.updateQuery(withDisplaySettings: query) let resultsContext = ClientContext(with: context, modifier: { context in context.query = query + if let useSortDescriptor { + context.sortDescriptor = useSortDescriptor + } }) let viewController = ClientItemViewController(context: resultsContext, query: query, showRevealButtonForItems: true, emptyItemListIcon: OCSymbol.icon(forSymbolName: "magnifyingglass"), emptyItemListTitleLocalized: "No matches".localized, emptyItemListMessageLocalized: "No items found matching the search criteria.".localized) diff --git a/ownCloudAppShared/Client/User Interface/SortBar.swift b/ownCloudAppShared/Client/User Interface/SortBar.swift index c63f09278..15ebb26b5 100644 --- a/ownCloudAppShared/Client/User Interface/SortBar.swift +++ b/ownCloudAppShared/Client/User Interface/SortBar.swift @@ -20,11 +20,7 @@ import UIKit import ownCloudSDK public protocol SortBarDelegate: AnyObject { - - var sortDirection: SortDirection { get set } - var sortMethod: SortMethod { get set } - - func sortBar(_ sortBar: SortBar, didUpdateSortMethod: SortMethod) + func sortBar(_ sortBar: SortBar, didChangeSortDescriptor: SortDescriptor) func sortBar(_ sortBar: SortBar, itemLayout: ItemLayout) func sortBarToggleSelectMode(_ sortBar: SortBar) } @@ -70,26 +66,30 @@ public class SortBar: ThemeCSSView { UIAccessibility.post(notification: .layoutChanged, argument: nil) } - public var sortMethod: SortMethod { + public var sortDescriptor: SortDescriptor { didSet { - if self.superview != nil { // Only toggle direction if the view is already in the view hierarchy (i.e. not during initial setup) - if oldValue == sortMethod { - if delegate?.sortDirection == .ascendant { - delegate?.sortDirection = .descendant - } else { - delegate?.sortDirection = .ascendant - } + updateSortButtonTitle() + } + } + + func userSelectedSortMethod(_ newSortMethod: SortMethod) { + var sortDirection = sortDescriptor.direction + + if self.superview != nil { // Only toggle direction if the view is already in the view hierarchy (i.e. not during initial setup) + if sortDescriptor.method == newSortMethod { + if sortDirection == .ascendant { + sortDirection = .descendant } else { - delegate?.sortDirection = .ascendant // Reset sort direction when switching sort methods + sortDirection = .ascendant } + } else { + sortDirection = .ascendant // Reset sort direction when switching sort methods } - updateSortButtonTitle() + } - sortButton?.accessibilityLabel = NSString(format: "Sort by %@".localized as NSString, sortMethod.localizedName) as String - sortButton?.sizeToFit() + sortDescriptor = SortDescriptor(method: newSortMethod, direction: sortDirection) - delegate?.sortBar(self, didUpdateSortMethod: sortMethod) - } + delegate?.sortBar(self, didChangeSortDescriptor: sortDescriptor) } public var itemLayout: ItemLayout = .list { @@ -100,12 +100,12 @@ public class SortBar: ThemeCSSView { } // MARK: - Init & Deinit - public init(frame: CGRect = .zero, sortMethod: SortMethod) { + public init(frame: CGRect = .zero, sortDescriptor: SortDescriptor) { selectButton = UIButton() changeItemLayoutButton = UIButton(type: .system) sortButton = UIButton(type: .system) - self.sortMethod = sortMethod + self.sortDescriptor = sortDescriptor super.init(frame: frame) self.cssSelector = .sortBar @@ -137,8 +137,8 @@ public class SortBar: ThemeCSSView { let title = method.localizedName var sortDirectionTitle = "" - if self?.delegate?.sortMethod == method { - if self?.delegate?.sortDirection == .ascendant { // Show arrows opposite to the current sort direction to show what choosing them will lead to + if self?.sortDescriptor.method == method { + if self?.sortDescriptor.direction == .ascendant { // Show arrows opposite to the current sort direction to show what choosing them will lead to sortDirectionTitle = " ↓" } else { sortDirectionTitle = " ↑" @@ -146,7 +146,7 @@ public class SortBar: ThemeCSSView { } let menuItem = UIAction(title: "\(title)\(sortDirectionTitle)", image: nil, attributes: []) { [weak self] _ in - self?.sortMethod = method + self?.userSelectedSortMethod(method) } menuItems.append(menuItem) @@ -229,12 +229,15 @@ public class SortBar: ThemeCSSView { // MARK: - Sort Direction Title func updateSortButtonTitle() { - let sortButtonTitle = sortDirectionTitle(sortMethod.localizedName) + let method = sortDescriptor.method + let sortButtonTitle = sortDirectionTitle(method.localizedName) sortButton?.setTitle(sortButtonTitle, for: .normal) + sortButton?.accessibilityLabel = NSString(format: "Sort by %@".localized as NSString, method.localizedName) as String + sortButton?.sizeToFit() } func sortDirectionTitle(_ title: String) -> String { - if delegate?.sortDirection == .descendant { + if sortDescriptor.direction == .descendant { return String(format: "%@ ↓", title) } else { return String(format: "%@ ↑", title) diff --git a/ownCloudAppShared/Client/User Interface/SortMethod.swift b/ownCloudAppShared/Client/User Interface/SortMethod.swift index 7f6a4cfa3..fce57b87f 100644 --- a/ownCloudAppShared/Client/User Interface/SortMethod.swift +++ b/ownCloudAppShared/Client/User Interface/SortMethod.swift @@ -33,8 +33,9 @@ public enum SortMethod: Int { case size = 2 case date = 3 case shared = 4 + case lastUsed = 5 - public static var all: [SortMethod] = [alphabetically, kind, size, date, shared] + public static var all: [SortMethod] = [alphabetically, kind, size, date, lastUsed, shared] public var localizedName : String { var name = "" @@ -50,6 +51,8 @@ public enum SortMethod: Int { name = "date".localized case .shared: name = "shared".localized + case .lastUsed: + name = "last used".localized } return name @@ -68,6 +71,8 @@ public enum SortMethod: Int { case .date: propertyName = .lastModified case .shared: break + case .lastUsed: + propertyName = .lastUsed } return propertyName @@ -107,85 +112,102 @@ public enum SortMethod: Int { } switch self { - case .size: - comparator = { (left, right) in - let leftItem = left as? OCItem - let rightItem = right as? OCItem - - let leftSize = leftItem!.size as NSNumber - let rightSize = rightItem!.size as NSNumber - if direction == .descendant { - return leftSize.compare(rightSize) + case .size: + comparator = { (left, right) in + let leftItem = left as? OCItem + let rightItem = right as? OCItem + + let leftSize = leftItem!.size as NSNumber + let rightSize = rightItem!.size as NSNumber + if direction == .descendant { + return leftSize.compare(rightSize) + } + + return rightSize.compare(leftSize) } - return rightSize.compare(leftSize) - } - case .alphabetically: - comparator = alphabeticComparator - case .kind: - comparator = { (left, right) in - let leftItem = left as? OCItem - let rightItem = right as? OCItem + case .alphabetically: + comparator = alphabeticComparator - let leftKind = leftItem?.fileExtension ?? leftItem?.mimeType ?? "_various" - let rightKind = rightItem?.fileExtension ?? rightItem?.mimeType ?? "_various" + case .kind: + comparator = { (left, right) in + let leftItem = left as? OCItem + let rightItem = right as? OCItem - var result : ComparisonResult = leftKind.compare(rightKind) + let leftKind = leftItem?.fileExtension ?? leftItem?.mimeType ?? "_various" + let rightKind = rightItem?.fileExtension ?? rightItem?.mimeType ?? "_various" - let typeResult = itemTypeComparator(left, right) + var result : ComparisonResult = leftKind.compare(rightKind) - if typeResult != .orderedSame { - result = typeResult - } + let typeResult = itemTypeComparator(left, right) - if direction == .descendant { - if result == .orderedDescending { - result = .orderedAscending - } else if result == .orderedAscending { - result = .orderedDescending + if typeResult != .orderedSame { + result = typeResult } + + if direction == .descendant { + if result == .orderedDescending { + result = .orderedAscending + } else if result == .orderedAscending { + result = .orderedDescending + } + } + + return result } - return result - } - case .shared: - comparator = { (left, right) in - guard let leftItem = left as? OCItem else { return .orderedSame } - guard let rightItem = right as? OCItem else { return .orderedSame } + case .shared: + comparator = { (left, right) in + guard let leftItem = left as? OCItem else { return .orderedSame } + guard let rightItem = right as? OCItem else { return .orderedSame } - let leftShared = leftItem.isSharedWithUser || leftItem.isShared - let rightShared = rightItem.isSharedWithUser || rightItem.isShared + let leftShared = leftItem.isSharedWithUser || leftItem.isShared + let rightShared = rightItem.isSharedWithUser || rightItem.isShared - if leftShared == rightShared { - return .orderedSame - } + if leftShared == rightShared { + return .orderedSame + } - if direction == .descendant { - if rightShared { - return .orderedAscending + if direction == .descendant { + if rightShared { + return .orderedAscending + } + + return .orderedDescending + } else { + if leftShared { + return .orderedAscending + } + + return .orderedDescending } + } - return .orderedDescending - } else { - if leftShared { - return .orderedAscending + case .date: + comparator = { (left, right) in + + guard let leftLastModified = (left as? OCItem)?.lastModified, let rightLastModified = (right as? OCItem)?.lastModified else { + return .orderedSame + } + if direction == .descendant { + return leftLastModified.compare(rightLastModified) } - return .orderedDescending + return rightLastModified.compare(leftLastModified) } - } - case .date: - comparator = { (left, right) in - guard let leftLastModified = (left as? OCItem)?.lastModified, let rightLastModified = (right as? OCItem)?.lastModified else { - return .orderedSame - } - if direction == .descendant { - return leftLastModified.compare(rightLastModified) - } + case .lastUsed: + comparator = { (left, right) in - return rightLastModified.compare(leftLastModified) - } + guard let leftLastUsed = (left as? OCItem)?.lastUsed ?? (left as? OCItem)?.lastModified, let rightLastUsed = (right as? OCItem)?.lastUsed ?? (right as? OCItem)?.lastModified else { + return .orderedSame + } + if direction == .descendant { + return leftLastUsed.compare(rightLastUsed) + } + + return rightLastUsed.compare(leftLastUsed) + } } if combinedComparator == nil { @@ -226,4 +248,18 @@ public class SortDescriptor: NSObject { public var comparator: OCSort { return method.comparator(direction: direction) } + + public static var defaultSortDescriptor: SortDescriptor { + get { + let defaultSortMethod: SortMethod = SortMethod(rawValue: UserDefaults.standard.integer(forKey: "sort-method")) ?? .alphabetically + let defaultSortDirection: SortDirection = SortDirection(rawValue: UserDefaults.standard.integer(forKey: "sort-direction")) ?? .ascendant + + return SortDescriptor(method: defaultSortMethod, direction: defaultSortDirection) + } + + set { + UserDefaults.standard.setValue(newValue.method.rawValue, forKey: "sort-method") + UserDefaults.standard.setValue(newValue.direction.rawValue, forKey: "sort-direction") + } + } } diff --git a/ownCloudAppShared/Client/View Controllers/ClientItemViewController.swift b/ownCloudAppShared/Client/View Controllers/ClientItemViewController.swift index a1d190b35..a255c3b47 100644 --- a/ownCloudAppShared/Client/View Controllers/ClientItemViewController.swift +++ b/ownCloudAppShared/Client/View Controllers/ClientItemViewController.swift @@ -84,6 +84,8 @@ open class ClientItemViewController: CollectionViewController, SortBarDelegate, let vcUUID = UUID() viewControllerUUID = vcUUID + sortDescriptor = inContext?.sortDescriptor ?? .defaultSortDescriptor + let itemControllerContext = ClientContext(with: inContext, modifier: { context in // Add permission handler limiting interactions for specific items and scenarios context.add(permissionHandler: { (context, record, interaction, viewController) in @@ -147,10 +149,8 @@ open class ClientItemViewController: CollectionViewController, SortBarDelegate, } context.query = (owner as? ClientItemViewController)?.query - if let sortMethod = (owner as? ClientItemViewController)?.sortMethod, - let sortDirection = (owner as? ClientItemViewController)?.sortDirection { - // Set default sort descriptor - context.sortDescriptor = SortDescriptor(method: sortMethod, direction: sortDirection) + if let sortDescriptor = (owner as? ClientItemViewController)?.sortDescriptor { + context.sortDescriptor = sortDescriptor } context.originatingViewController = owner as? UIViewController @@ -302,7 +302,7 @@ open class ClientItemViewController: CollectionViewController, SortBarDelegate, } // Initialize sort method - handleSortMethodChange() + applySortDescriptor() // Update title updateNavigationTitleFromContext() @@ -337,10 +337,9 @@ open class ClientItemViewController: CollectionViewController, SortBarDelegate, updateNavigationBarButtonItems() // Setup sort bar - sortBar = SortBar(sortMethod: sortMethod) + sortBar = SortBar(sortDescriptor: sortDescriptor) sortBar?.translatesAutoresizingMaskIntoConstraints = false sortBar?.delegate = self - sortBar?.sortMethod = sortMethod sortBar?.itemLayout = clientContext?.itemLayout ?? .list sortBar?.showSelectButton = true @@ -706,40 +705,21 @@ open class ClientItemViewController: CollectionViewController, SortBarDelegate, // MARK: - Sorting open var sortBar: SortBar? - open var sortMethod: SortMethod { - set { - UserDefaults.standard.setValue(newValue.rawValue, forKey: "sort-method") - handleSortMethodChange() - } - - get { - let sort = SortMethod(rawValue: UserDefaults.standard.integer(forKey: "sort-method")) ?? SortMethod.alphabetically - return sort - } - } - open var sortDirection: SortDirection { - set { - UserDefaults.standard.setValue(newValue.rawValue, forKey: "sort-direction") - } + open var sortDescriptor: SortDescriptor - get { - let direction = SortDirection(rawValue: UserDefaults.standard.integer(forKey: "sort-direction")) ?? SortDirection.ascendant - return direction - } - } - open func handleSortMethodChange() { - let sortDescriptor = SortDescriptor(method: sortMethod, direction: sortDirection) + public func sortBar(_ sortBar: SortBar, didChangeSortDescriptor newSortDescriptor: SortDescriptor) { + sortDescriptor = newSortDescriptor - clientContext?.sortDescriptor = sortDescriptor - query?.sortComparator = sortDescriptor.comparator - } + clientContext?.sortDescriptor = newSortDescriptor + itemSection?.clientContext?.sortDescriptor = newSortDescriptor // Also needs to change the sortDescriptor for the itemSection's clientContext, since that is what will be used when creating ClientItemViewControllers for subfolders, etc. - public func sortBar(_ sortBar: SortBar, didUpdateSortMethod: SortMethod) { - sortMethod = didUpdateSortMethod + SortDescriptor.defaultSortDescriptor = newSortDescriptor // Change default ONLY for user-initiated changes - let comparator = sortMethod.comparator(direction: sortDirection) + applySortDescriptor() + } - query?.sortComparator = comparator + open func applySortDescriptor() { + query?.sortComparator = sortDescriptor.comparator } public func sortBar(_ sortBar: SortBar, itemLayout: ItemLayout) { diff --git a/ownCloudAppShared/SDK Extensions/OCCore+Extension.swift b/ownCloudAppShared/SDK Extensions/OCCore+Extension.swift index 435723b03..dcd83400e 100644 --- a/ownCloudAppShared/SDK Extensions/OCCore+Extension.swift +++ b/ownCloudAppShared/SDK Extensions/OCCore+Extension.swift @@ -84,4 +84,15 @@ extension OCCore { return parentItems.reversed() } + + public func updateLastUsed(for inItem: OCItem) { + schedule(inCoreQueue: { [weak self] in + if let self, let location = inItem.location, let item = try? self.cachedItem(at: location) { + item.lastUsed = .now + item.updateSeed() + + self.performUpdates(forAddedItems: nil, removedItems: nil, updatedItems: [item], refreshLocations: nil, newSyncAnchor: nil, beforeQueryUpdates: nil, afterQueryUpdates: nil, queryPostProcessor: nil, skipDatabase: false) + } + }) + } } diff --git a/ownCloudAppShared/User Interface/EmbeddingViewController/EmbeddingViewController.swift b/ownCloudAppShared/User Interface/EmbeddingViewController/EmbeddingViewController.swift index b69748d9f..0913359ba 100644 --- a/ownCloudAppShared/User Interface/EmbeddingViewController/EmbeddingViewController.swift +++ b/ownCloudAppShared/User Interface/EmbeddingViewController/EmbeddingViewController.swift @@ -59,7 +59,7 @@ open class EmbeddingViewController: UIViewController { contentViewControllerConstraints = nil } didSet { - if let contentViewController = contentViewController, let contentViewControllerView = contentViewController.view { + if let contentViewController, let contentViewControllerView = contentViewController.view { addChild(contentViewController) addContentViewControllerSubview(contentViewControllerView) contentViewControllerView.translatesAutoresizingMaskIntoConstraints = false