diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 876dfa933f..b04889390c 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -67,6 +67,7 @@ D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; }; D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */; }; D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */; }; + D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */; }; D65A37F321472F300087646E /* SwiftSoup.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BED16E212663DA00F02DA0 /* SwiftSoup.framework */; }; D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; }; D663625F2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */; }; @@ -221,6 +222,7 @@ D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = ""; }; D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = ""; }; D64D0AB02128D9AE005A6F37 /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; + D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSwipeActionProvider.swift; sourceTree = ""; }; D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConversationMainStatusTableViewCell.xib; sourceTree = ""; }; D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMainStatusTableViewCell.swift; sourceTree = ""; }; D663626121360B1900C9CBA2 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; @@ -571,6 +573,7 @@ D6333B762138D94E00CE884A /* ComposeMediaView.swift */, D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */, 04ED00B021481ED800567C53 /* SteppedProgressView.swift */, + D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */, D641C78A213DD926004B4513 /* Status */, D641C78B213DD92F004B4513 /* Profile Header */, D641C78C213DD937004B4513 /* Notifications */, @@ -926,6 +929,7 @@ D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */, 04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */, D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */, + D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */, D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */, D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */, D6C94D892139E6EC00CB5196 /* AttachmentView.swift in Sources */, diff --git a/Tusker/Screens/Conversation/ConversationViewController.swift b/Tusker/Screens/Conversation/ConversationViewController.swift index 99c4f6cd07..53b3983596 100644 --- a/Tusker/Screens/Conversation/ConversationViewController.swift +++ b/Tusker/Screens/Conversation/ConversationViewController.swift @@ -117,6 +117,18 @@ class ConversationViewController: UIViewController, UITableViewDataSource, UITab let status = statuses[indexPath.row] return status == mainStatus ? nil : indexPath } + + func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return tableView.cellForRow(at: indexPath) is TableViewSwipeActionProvider + } + + func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration() + } + + func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() + } } diff --git a/Tusker/Screens/Notifications/NotificationsTableViewController.swift b/Tusker/Screens/Notifications/NotificationsTableViewController.swift index c1e785d1d5..ef6b3d4dda 100644 --- a/Tusker/Screens/Notifications/NotificationsTableViewController.swift +++ b/Tusker/Screens/Notifications/NotificationsTableViewController.swift @@ -118,6 +118,18 @@ class NotificationsTableViewController: UITableViewController { } } + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return tableView.cellForRow(at: indexPath) is TableViewSwipeActionProvider + } + + override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration() + } + + override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() + } + @IBAction func refreshNotifications(_ sender: Any) { guard let newer = newer else { return } diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index 4927442e0e..9f67d77586 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -132,6 +132,18 @@ class ProfileTableViewController: UITableViewController, PreferencesAdaptive { } } + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return tableView.cellForRow(at: indexPath) is TableViewSwipeActionProvider + } + + override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration() + } + + override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() + } + @IBAction func refreshStatuses(_ sender: Any) { guard let newer = newer else { return } diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index 59a5dfdb62..792e844eec 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -133,107 +133,16 @@ class TimelineTableViewController: UITableViewController { } } + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return tableView.cellForRow(at: indexPath) is TableViewSwipeActionProvider + } + override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let status = statuses[indexPath.row] - - let favorite: UIContextualAction - if status.favourited ?? false { - favorite = UIContextualAction(style: .normal, title: "Unfavorite", handler: { (action, view, completion) in - status.unfavourite(completion: { response in - DispatchQueue.main.async { - if case .success = response { - completion(true) - guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { return } - cell.updateUI(for: cell.status) - } else { - completion(false) - } - } - }) - }) - favorite.backgroundColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) - } else { - favorite = UIContextualAction(style: .normal, title: "Favorite") { (action, view, completion) in - status.favourite(completion: { response in - DispatchQueue.main.async { - if case .success = response { - completion(true) - guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { return } - cell.updateUI(for: cell.status) - } else { - completion(false) - } - } - }) - } - favorite.backgroundColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1) - } - favorite.image = favoriteActionImage - - let reblog: UIContextualAction - if status.reblogged ?? false { - reblog = UIContextualAction(style: .normal, title: "Unreblog", handler: { (action, view, completion) in - status.unreblog(completion: { response in - DispatchQueue.main.async { - if case .success = response { - completion(true) - guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { return } - cell.updateUI(for: cell.status) - } else { - completion(false) - } - } - }) - }) - reblog.backgroundColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) - } else { - reblog = UIContextualAction(style: .normal, title: "Reblog") { (action, view, completion) in - status.reblog(completion: { response in - DispatchQueue.main.async { - if case .success = response { - completion(true) - guard let cell = tableView.cellForRow(at: indexPath) as? StatusTableViewCell else { return } - cell.updateUI(for: cell.status) - } else { - completion(false) - } - } - }) - } - reblog.backgroundColor = view.tintColor - } - reblog.image = reblogActionImage - - - let actions = [ - favorite, - reblog - ] - return UISwipeActionsConfiguration(actions: actions) + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.leadingSwipeActionsConfiguration() } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let status = statuses[indexPath.row] - - let reply = UIContextualAction(style: .normal, title: "Reply") { (action, view, completion) in - completion(true) - self.reply(to: status) - } - reply.image = replyActionImage - reply.backgroundColor = view.tintColor - - let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in - completion(true) - self.showMoreOptions(status: status) - } - more.image = moreActionImage - more.backgroundColor = .gray - - let actions = [ - reply, - more - ] - return UISwipeActionsConfiguration(actions: actions) + return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() } @IBAction func refreshStatuses(_ sender: Any) { diff --git a/Tusker/Views/Status/StatusTableViewCell.swift b/Tusker/Views/Status/StatusTableViewCell.swift index 72fdd75896..54154fb584 100644 --- a/Tusker/Views/Status/StatusTableViewCell.swift +++ b/Tusker/Views/Status/StatusTableViewCell.swift @@ -215,8 +215,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive { let realStatus: Status = status.reblog ?? status (favorited ? realStatus.favourite : realStatus.unfavourite)() { response in - self.favorited = realStatus.favourited ?? false DispatchQueue.main.async { + self.favorited = realStatus.favourited ?? false if case .success = response { UIImpactFeedbackGenerator(style: .light).impactOccurred() } else { @@ -235,8 +235,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive { let realStatus: Status = status.reblog ?? status (reblogged ? realStatus.reblog : realStatus.unreblog)() { response in - self.reblogged = realStatus.reblogged ?? false DispatchQueue.main.async { + self.reblogged = realStatus.reblogged ?? false if case .success = response { UIImpactFeedbackGenerator(style: .light).impactOccurred() } else { @@ -254,6 +254,99 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive { } +extension StatusTableViewCell: TableViewSwipeActionProvider { + + static var favoriteActionImage: UIImage = UIGraphicsImageRenderer(size: CGSize(width: 30 * 137/131, height: 30)).image { _ in + UIImage(named: "Favorite")!.draw(in: CGRect(x: 0, y: 0, width: 30 * 137/131, height: 30)) + } + static var reblogActionImage: UIImage = UIGraphicsImageRenderer(size: CGSize(width: 30 * 927/558, height: 30)).image { _ in + UIImage(named: "Reblog")!.draw(in: CGRect(x: 0, y: 0, width: 30 * 927/558, height: 30)) + } + static var replyActionImage: UIImage = UIGraphicsImageRenderer(size: CGSize(width: 30 * 205/151, height: 30)).image { _ in + UIImage(named: "Reply")!.draw(in: CGRect(x: 0, y: 0, width: 30 * 205/151, height: 30)) + } + static var moreActionImage: UIImage = UIGraphicsImageRenderer(size: CGSize(width: 30 * 2/1, height: 30)).image { _ in + UIImage(named: "More")!.draw(in: CGRect(x: 0, y: 0, width: 30 * 2/1, height: 30)) + } + + func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? { + let favoriteTitle: String + let favoriteAction: (@escaping Client.Callback) -> Void + let favoriteColor: UIColor + if status.favourited ?? false { + favoriteTitle = "Unfavorite" + favoriteAction = status.unfavourite + favoriteColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) + + } else { + favoriteTitle = "Favorite" + favoriteAction = status.favourite + favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1) + } + let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in + favoriteAction { response in + DispatchQueue.main.async { + if case .success = response { + completion(true) + self.updateUI(for: self.status) + } else { + completion(false) + } + } + } + } + favorite.image = StatusTableViewCell.favoriteActionImage + favorite.backgroundColor = favoriteColor + + let reblogTitle: String + let reblogAction: (@escaping Client.Callback) -> Void + let reblogColor: UIColor + if status.reblogged ?? false { + reblogTitle = "Unreblog" + reblogAction = status.unreblog + reblogColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) + } else { + reblogTitle = "Reblog" + reblogAction = status.reblog + reblogColor = tintColor + } + let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in + reblogAction { response in + DispatchQueue.main.async { + if case .success = response { + completion(true) + self.updateUI(for: self.status) + } else { + completion(false) + } + } + } + } + reblog.image = StatusTableViewCell.reblogActionImage + reblog.backgroundColor = reblogColor + + return UISwipeActionsConfiguration(actions: [favorite, reblog]) + } + + func trailingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? { + let reply = UIContextualAction(style: .normal, title: "Reply") { (action, view, completion) in + completion(true) + self.delegate?.reply(to: self.status) + } + reply.image = StatusTableViewCell.replyActionImage + reply.backgroundColor = tintColor + let more = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in + completion(true) + self.delegate?.showMoreOptions(status: self.status) + } + more.image = StatusTableViewCell.moreActionImage + more.backgroundColor = .gray + return UISwipeActionsConfiguration(actions: [reply, more]) + } + + +} + extension StatusTableViewCell: HTMLContentLabelDelegate { func selected(mention: Mention) { diff --git a/Tusker/Views/TableViewSwipeActionProvider.swift b/Tusker/Views/TableViewSwipeActionProvider.swift new file mode 100644 index 0000000000..c2554cd9b1 --- /dev/null +++ b/Tusker/Views/TableViewSwipeActionProvider.swift @@ -0,0 +1,17 @@ +// +// TableViewSwipeActionProvider.swift +// Tusker +// +// Created by Shadowfacts on 9/15/18. +// Copyright © 2018 Shadowfacts. All rights reserved. +// + +import UIKit + +protocol TableViewSwipeActionProvider { + + func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? + + func trailingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? + +}