From 26fc5f4666c943a37324f036818fbf5781af81c8 Mon Sep 17 00:00:00 2001 From: Pablo Carrascal Date: Thu, 2 Aug 2018 10:06:56 +0200 Subject: [PATCH 1/2] - Fixed lint warnings in TVGImage.swift and Log.swift files. - Created a swipe gesture for delete files and folders. - Created new convenience init for UIAlertController to be able to create delete-cancel alert controllers more easily. --- .../Client/ClientQueryViewController.swift | 168 +++++++++++------- .../Resources/en.lproj/Localizable.strings | 2 + ownCloud/Theming/TVG/TVGImage.swift | 20 +-- ownCloud/Tools/Log.swift | 18 +- .../UIAlertController+OCConnectionIssue.swift | 27 ++- 5 files changed, 143 insertions(+), 92 deletions(-) diff --git a/ownCloud/Client/ClientQueryViewController.swift b/ownCloud/Client/ClientQueryViewController.swift index c3f1e05b8..49d01f8dc 100644 --- a/ownCloud/Client/ClientQueryViewController.swift +++ b/ownCloud/Client/ClientQueryViewController.swift @@ -7,14 +7,14 @@ // /* - * Copyright (C) 2018, 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 . - * - */ +* Copyright (C) 2018, 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 @@ -167,47 +167,47 @@ class ClientQueryViewController: UITableViewController, Themeable { var summary : ProgressSummary = ProgressSummary(indeterminate: true, progress: 1.0, message: nil, progressCount: 1) switch query?.state { - case .stopped?: - summary.message = "Stopped".localized + case .stopped?: + summary.message = "Stopped".localized - case .started?: - summary.message = "Started…".localized + case .started?: + summary.message = "Started…".localized - case .contentsFromCache?: - if core?.reachabilityMonitor?.available == true { - summary.message = "Contents from cache.".localized - } else { - summary.message = "Offline. Contents from cache.".localized - } + case .contentsFromCache?: + if core?.reachabilityMonitor?.available == true { + summary.message = "Contents from cache.".localized + } else { + summary.message = "Offline. Contents from cache.".localized + } - case .waitingForServerReply?: - summary.message = "Waiting for server response…".localized + case .waitingForServerReply?: + summary.message = "Waiting for server response…".localized - case .targetRemoved?: - summary.message = "This folder no longer exists.".localized + case .targetRemoved?: + summary.message = "This folder no longer exists.".localized - case .idle?: - summary.message = "Everything up-to-date.".localized - summary.progressCount = 0 + case .idle?: + summary.message = "Everything up-to-date.".localized + summary.progressCount = 0 - case .none: - summary.message = "Please wait…".localized + case .none: + summary.message = "Please wait…".localized } switch query?.state { - case .idle?: - DispatchQueue.main.async { - if !self.refreshController!.isRefreshing { - self.refreshController?.beginRefreshing() - } + case .idle?: + DispatchQueue.main.async { + if !self.refreshController!.isRefreshing { + self.refreshController?.beginRefreshing() } + } - case .contentsFromCache?, .stopped?: - DispatchQueue.main.async { - self.tableView.refreshControl = nil - } + case .contentsFromCache?, .stopped?: + DispatchQueue.main.async { + self.tableView.refreshControl = nil + } - default: + default: break } @@ -256,39 +256,79 @@ class ClientQueryViewController: UITableViewController, Themeable { } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let rowItem : OCItem = self.items![indexPath.row] - switch rowItem.type { - case .collection: - self.navigationController?.pushViewController(ClientQueryViewController(core: self.core!, query: OCQuery(forPath: rowItem.path)), animated: true) + guard let rowItem : OCItem = self.items?[indexPath.row] else { + return + } - case .file: - let fallbackSummary = ProgressSummary(indeterminate: true, progress: 1.0, message: "Downloading \(rowItem.name)", progressCount: 1) + switch rowItem.type { + case .collection: + self.navigationController?.pushViewController(ClientQueryViewController(core: self.core!, query: OCQuery(forPath: rowItem.path)), animated: true) - if let downloadProgress = self.core?.downloadItem(rowItem, options: nil, resultHandler: { (error, _, item, file) in - OnMainThread { - if error != nil { - // TODO: Handle error - } else { - let itemViewController : ClientItemViewController = ClientItemViewController() + case .file: + let fallbackSummary = ProgressSummary(indeterminate: true, progress: 1.0, message: "Downloading \(rowItem.name!)", progressCount: 1) - itemViewController.file = file - itemViewController.item = item + if let downloadProgress = self.core?.downloadItem(rowItem, options: nil, resultHandler: { (error, _, item, file) in + OnMainThread { + if error != nil { + // TODO: Handle error + } else { + let itemViewController : ClientItemViewController = ClientItemViewController() - self.navigationController?.pushViewController(itemViewController, animated: true) - } + itemViewController.file = file + itemViewController.item = item - self.progressSummarizer?.popFallbackSummary(summary: fallbackSummary) + self.navigationController?.pushViewController(itemViewController, animated: true) } - }) { - Log.log("Downloading \(rowItem.name): \(downloadProgress)") - - progressSummarizer?.pushFallbackSummary(summary: fallbackSummary) - // TODO: Use progress as soon as it works SDK-wise - // progressSummarizer?.startTracking(progress: downloadProgress) + self.progressSummarizer?.popFallbackSummary(summary: fallbackSummary) } + }) { + Log.log("Downloading \(rowItem.name!): \(downloadProgress)") + + progressSummarizer?.pushFallbackSummary(summary: fallbackSummary) + + // TODO: Use progress as soon as it works SDK-wise + // progressSummarizer?.startTracking(progress: downloadProgress) + } + } + } + + override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + + guard let item: OCItem = items?[indexPath.row], core != nil else { + return nil + } + + var summary : ProgressSummary = ProgressSummary(indeterminate: true, progress: 1.0, message: nil, progressCount: 1) + + let presentationStyle: UIAlertControllerStyle = UIDevice.current.isIpad() ? UIAlertControllerStyle.alert : UIAlertControllerStyle.actionSheet + + let deleteContextualAction: UIContextualAction = UIContextualAction(style: .destructive, title: "Delete".localized) { (_, _, actionPerformed) in + + let alertController = + UIAlertController(with: item.name!, message: "Are you sure you want to delete this file from the server?".localized, preferredStyle: presentationStyle, destructiveAction: { + summary.message = NSString(format: "Deleting '%@'".localized as NSString, item.name as NSString) as String + + self.queryProgressSummary = summary + + self.core?.delete(item, requireMatch: true, resultHandler: { (error, _, _, _) in + summary.progressCount = 0 + self.queryProgressSummary = summary + if error != nil { + Log.log("Error \(String(describing: error)) deleting \(String(describing: item.path))") + } + }) + }) + + self.present(alertController, animated: true, completion: { + actionPerformed(false) + }) } + let actions: [UIContextualAction] = [deleteContextualAction] + let actionsConfigurator: UISwipeActionsConfiguration = UISwipeActionsConfiguration(actions: actions) + + return actionsConfigurator } // MARK: - Message @@ -336,10 +376,10 @@ class ClientQueryViewController: UITableViewController, Themeable { containerView.addSubview(messageLabel) containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[imageView]-(20)-[titleLabel]-[messageLabel]|", - options: NSLayoutFormatOptions(rawValue: 0), - metrics: nil, - views: ["imageView" : imageView, "titleLabel" : titleLabel, "messageLabel" : messageLabel]) - ) + options: NSLayoutFormatOptions(rawValue: 0), + metrics: nil, + views: ["imageView" : imageView, "titleLabel" : titleLabel, "messageLabel" : messageLabel]) + ) imageView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true imageView.widthAnchor.constraint(equalToConstant: 96).isActive = true diff --git a/ownCloud/Resources/en.lproj/Localizable.strings b/ownCloud/Resources/en.lproj/Localizable.strings index d2c0a7d1c..f3d5cdf61 100644 --- a/ownCloud/Resources/en.lproj/Localizable.strings +++ b/ownCloud/Resources/en.lproj/Localizable.strings @@ -76,6 +76,8 @@ "Folder removed" = "Folder removed"; "This folder no longer exists on the server." = "This folder no longer exists on the server."; +"Are you sure you want to delete this file from the server?" = "Are you sure you want to delete this file from the server?"; +"Deleting '%@'" = "Deleting '%@'"; /* Server List*/ "Cancel" = "Cancel"; diff --git a/ownCloud/Theming/TVG/TVGImage.swift b/ownCloud/Theming/TVG/TVGImage.swift index 664f853a9..5989f581a 100644 --- a/ownCloud/Theming/TVG/TVGImage.swift +++ b/ownCloud/Theming/TVG/TVGImage.swift @@ -7,14 +7,14 @@ // /* - * Copyright (C) 2018, 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 . - * - */ +* Copyright (C) 2018, 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 PocketSVG @@ -103,9 +103,7 @@ class TVGImage: NSObject { return nil } - guard let bezierPaths : [SVGBezierPath] = SVGBezierPath.paths(fromSVGString: svgString) as? [SVGBezierPath] else { - return nil - } + let bezierPaths : [SVGBezierPath] = SVGBezierPath.paths(fromSVGString: svgString) svgBezierPaths = bezierPaths diff --git a/ownCloud/Tools/Log.swift b/ownCloud/Tools/Log.swift index 070676f09..40527e4de 100644 --- a/ownCloud/Tools/Log.swift +++ b/ownCloud/Tools/Log.swift @@ -7,14 +7,14 @@ // /* - * Copyright (C) 2018, 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 . - * - */ +* Copyright (C) 2018, 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 @@ -46,6 +46,6 @@ class Log { } static func mask(_ obj: Any?) -> Any { - return (OCLogger.shared.applyPrivacyMask(obj)) + return (OCLogger.shared.applyPrivacyMask(obj))! } } diff --git a/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift b/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift index 44a893d1f..4b9391b64 100644 --- a/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift +++ b/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift @@ -12,14 +12,12 @@ import ownCloudSDK extension OCConnectionIssueChoice { var alertActionStyle : UIAlertActionStyle { switch type { - case .cancel: - return .cancel - - case .regular, .default: - return .default - - case .destructive: - return .destructive + case .cancel: + return .cancel + case .regular, .default: + return .default + case .destructive: + return .destructive } } } @@ -35,4 +33,17 @@ extension UIAlertController { })) } } + + convenience init(with title: String, message: String, preferredStyle: UIAlertControllerStyle, destructiveAction action: @escaping () -> Void) { + + self.init(title: title, message: message, preferredStyle: preferredStyle) + + let cancelAction = UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil) + let destructiveAction = UIAlertAction(title: "Delete".localized, style: .destructive) { (_) in + action() + } + + self.addAction(destructiveAction) + self.addAction(cancelAction) + } } From 019ec35de10a04a0f2bca037335b2f3ff63335f4 Mon Sep 17 00:00:00 2001 From: Felix Schwarz Date: Tue, 7 Aug 2018 22:25:03 +0200 Subject: [PATCH 2/2] - Change delete support to utilize the returned progress object for summarizing progress - Change UIAlertController category so it is reusable by adding parameters for cancel and destructive labels --- .../Client/ClientQueryViewController.swift | 27 +++++++++---------- .../UIAlertController+OCConnectionIssue.swift | 6 ++--- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/ownCloud/Client/ClientQueryViewController.swift b/ownCloud/Client/ClientQueryViewController.swift index 49d01f8dc..054c2be05 100644 --- a/ownCloud/Client/ClientQueryViewController.swift +++ b/ownCloud/Client/ClientQueryViewController.swift @@ -300,26 +300,25 @@ class ClientQueryViewController: UITableViewController, Themeable { return nil } - var summary : ProgressSummary = ProgressSummary(indeterminate: true, progress: 1.0, message: nil, progressCount: 1) - let presentationStyle: UIAlertControllerStyle = UIDevice.current.isIpad() ? UIAlertControllerStyle.alert : UIAlertControllerStyle.actionSheet let deleteContextualAction: UIContextualAction = UIContextualAction(style: .destructive, title: "Delete".localized) { (_, _, actionPerformed) in let alertController = - UIAlertController(with: item.name!, message: "Are you sure you want to delete this file from the server?".localized, preferredStyle: presentationStyle, destructiveAction: { - summary.message = NSString(format: "Deleting '%@'".localized as NSString, item.name as NSString) as String - - self.queryProgressSummary = summary - - self.core?.delete(item, requireMatch: true, resultHandler: { (error, _, _, _) in - summary.progressCount = 0 - self.queryProgressSummary = summary - if error != nil { - Log.log("Error \(String(describing: error)) deleting \(String(describing: item.path))") + UIAlertController(with: item.name!, + message: "Are you sure you want to delete this file from the server?".localized, + destructiveLabel: "Delete".localized, + preferredStyle: presentationStyle, + destructiveAction: { + if let progress = self.core?.delete(item, requireMatch: true, resultHandler: { (error, _, _, _) in + if error != nil { + Log.log("Error \(String(describing: error)) deleting \(String(describing: item.path))") + } + }) { + self.progressSummarizer?.startTracking(progress: progress) } - }) - }) + } + ) self.present(alertController, animated: true, completion: { actionPerformed(false) diff --git a/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift b/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift index 4b9391b64..98e35b103 100644 --- a/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift +++ b/ownCloud/UIKit Extensions/UIAlertController+OCConnectionIssue.swift @@ -34,12 +34,12 @@ extension UIAlertController { } } - convenience init(with title: String, message: String, preferredStyle: UIAlertControllerStyle, destructiveAction action: @escaping () -> Void) { + convenience init(with title: String, message: String, cancelLabel: String = "Cancel".localized, destructiveLabel: String, preferredStyle: UIAlertControllerStyle, destructiveAction action: @escaping () -> Void) { self.init(title: title, message: message, preferredStyle: preferredStyle) - let cancelAction = UIAlertAction(title: "Cancel".localized, style: .cancel, handler: nil) - let destructiveAction = UIAlertAction(title: "Delete".localized, style: .destructive) { (_) in + let cancelAction = UIAlertAction(title: cancelLabel, style: .cancel, handler: nil) + let destructiveAction = UIAlertAction(title: destructiveLabel, style: .destructive) { (_) in action() }