From d46b61595ede915e9af2b2b142487b7c62888f21 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 14 Sep 2018 11:35:00 -0400 Subject: [PATCH 1/4] Pachyderm: Fix pagination --- Pachyderm/Model/Timeline.swift | 25 ++++++++++++++----------- Pachyderm/Request/Parameter.swift | 3 ++- Pachyderm/Request/Request.swift | 27 ++++++++++++++++++--------- Pachyderm/Response/Pagination.swift | 4 ++-- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Pachyderm/Model/Timeline.swift b/Pachyderm/Model/Timeline.swift index e1eb7794..ba4eb047 100644 --- a/Pachyderm/Model/Timeline.swift +++ b/Pachyderm/Model/Timeline.swift @@ -17,22 +17,25 @@ public enum Timeline { } extension Timeline { - func request(range: RequestRange) -> Request<[Status]> { - var request: Request<[Status]> + var endpoint: String { switch self { case .home: - request = Request(method: .get, path: "/api/v1/timelines/home") - case let .public(local): - request = Request(method: .get, path: "/api/v1/timelines/public") - if local { - request.queryParameters.append("local" => true) - } + return "/api/v1/timelines/home" + case .public: + return "/api/v1/timelines/public" case let .tag(hashtag): - request = Request(method: .get, path: "/api/v1/timeliens/tag/\(hashtag)") + return "/api/v1/timelines/tag/\(hashtag)" case let .list(id): - request = Request(method: .get, path: "/api/v1/timelines/list/\(id)") + return "/api/v1/timelines/list/\(id)" case .direct: - request = Request(method: .get, path: "/api/v1/timelines/direct") + return "/api/v1/timelines/direct" + } + } + + func request(range: RequestRange) -> Request<[Status]> { + var request = Request<[Status]>(method: .get, path: endpoint) + if case .public(true) = self { + request.queryParameters.append("local" => true) } request.range = range return request diff --git a/Pachyderm/Request/Parameter.swift b/Pachyderm/Request/Parameter.swift index c6362765..8c03b9a5 100644 --- a/Pachyderm/Request/Parameter.swift +++ b/Pachyderm/Request/Parameter.swift @@ -74,7 +74,8 @@ extension Array where Element == Parameter { var queryItems: [URLQueryItem] { return compactMap { - URLQueryItem(name: $0.name, value: $0.value) + guard let value = $0.value else { return nil } + return URLQueryItem(name: $0.name, value: value) } } } diff --git a/Pachyderm/Request/Request.swift b/Pachyderm/Request/Request.swift index c9b3a1b0..522369d3 100644 --- a/Pachyderm/Request/Request.swift +++ b/Pachyderm/Request/Request.swift @@ -40,17 +40,26 @@ extension Request { } set { let rangeParams = newValue.queryParameters - let max = rangeParams.first { $0.name == "max_id" } - let since = rangeParams.first { $0.name == "since_id" } - let count = rangeParams.first { $0.name == "count" } - if let max = max, let i = queryParameters.firstIndex(where: { $0.name == "max_id" }) { - queryParameters[i] = max + if let max = rangeParams.first(where: { $0.name == "max_id" }) { + if let i = queryParameters.firstIndex(where: { $0.name == "max_id" }) { + queryParameters[i] = max + } else { + queryParameters.append(max) + } } - if let since = since, let i = queryParameters.firstIndex(where: { $0.name == "since_id" }) { - queryParameters[i] = since + if let since = rangeParams.first(where: { $0.name == "since_id" }) { + if let i = queryParameters.firstIndex(where: { $0.name == "since_id" }) { + queryParameters[i] = since + } else { + queryParameters.append(since) + } } - if let count = count, let i = queryParameters.firstIndex(where: { $0.name == "count" }) { - queryParameters[i] = count + if let count = rangeParams.first(where: { $0.name == "count" }) { + if let i = queryParameters.firstIndex(where: { $0.name == "count" }) { + queryParameters[i] = count + } else { + queryParameters.append(count) + } } } } diff --git a/Pachyderm/Response/Pagination.swift b/Pachyderm/Response/Pagination.swift index c0ec4cec..07d86a54 100644 --- a/Pachyderm/Response/Pagination.swift +++ b/Pachyderm/Response/Pagination.swift @@ -30,9 +30,9 @@ extension Pagination { var range: RequestRange { switch kind { case .next: - return .after(id: id, count: limit) - case .prev: return .before(id: id, count: limit) + case .prev: + return .after(id: id, count: limit) } } From 22dfec0483cbd25dca7374149c232babe1ab5a47 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 15 Sep 2018 10:56:27 -0400 Subject: [PATCH 2/4] Start work on swipe actions --- Pachyderm/Model/Status.swift | 4 ++ .../TimelineTableViewController.swift | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Pachyderm/Model/Status.swift b/Pachyderm/Model/Status.swift index 28b5bc3a..ca65c03f 100644 --- a/Pachyderm/Model/Status.swift +++ b/Pachyderm/Model/Status.swift @@ -111,8 +111,10 @@ public class Status: Decodable, ClientModel { client.run(request) { response in if case .success = response { self.favourited = true + self.reblog?.favourited = true } else { self.favourited = oldValue + self.reblog?.favourited = oldValue } completion(response) } @@ -124,8 +126,10 @@ public class Status: Decodable, ClientModel { client.run(request) { response in if case .success = response { self.favourited = false + self.reblog?.favourited = false } else { self.favourited = oldValue + self.reblog?.favourited = oldValue } completion(response) } diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index 2a6a7aa5..65963578 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -35,6 +35,13 @@ class TimelineTableViewController: UITableViewController { return navigationController } + lazy 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)) + } + lazy 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)) + } + var timeline: Timeline! var statuses: [Status] = [] { @@ -120,6 +127,47 @@ class TimelineTableViewController: UITableViewController { } } + override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + let status = statuses[indexPath.row] + + let 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.image = favoriteActionImage + favorite.backgroundColor = UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) + let 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.image = reblogActionImage + reblog.backgroundColor = view.tintColor + + let actions = [ + favorite, + reblog + ] + return UISwipeActionsConfiguration(actions: actions) + } + @IBAction func refreshStatuses(_ sender: Any) { guard let newer = newer else { return } From 4c9be9116246633464138127f16c2ad8cc18e9d1 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 15 Sep 2018 11:37:20 -0400 Subject: [PATCH 3/4] More swipe actions --- .../TimelineTableViewController.swift | 112 ++++++++++++++---- 1 file changed, 90 insertions(+), 22 deletions(-) diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index 65963578..59a5dfdb 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -41,6 +41,12 @@ class TimelineTableViewController: UITableViewController { lazy 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)) } + lazy 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)) + } + lazy 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)) + } var timeline: Timeline! @@ -130,36 +136,74 @@ class TimelineTableViewController: UITableViewController { override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let status = statuses[indexPath.row] - let 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) + 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 - favorite.backgroundColor = UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) - let 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) + + 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 - reblog.backgroundColor = view.tintColor + let actions = [ favorite, @@ -168,6 +212,30 @@ class TimelineTableViewController: UITableViewController { return UISwipeActionsConfiguration(actions: actions) } + 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) + } + @IBAction func refreshStatuses(_ sender: Any) { guard let newer = newer else { return } From 6ec0ae5489faa3a7e705026ecbd000df638a7c4e Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 15 Sep 2018 13:01:13 -0400 Subject: [PATCH 4/4] Refactor swipe actions into TableViewSwipeActionProvider --- Tusker.xcodeproj/project.pbxproj | 4 + .../ConversationViewController.swift | 12 ++ .../NotificationsTableViewController.swift | 12 ++ .../Profile/ProfileTableViewController.swift | 12 ++ .../TimelineTableViewController.swift | 103 +----------------- Tusker/Views/Status/StatusTableViewCell.swift | 97 ++++++++++++++++- .../Views/TableViewSwipeActionProvider.swift | 17 +++ 7 files changed, 158 insertions(+), 99 deletions(-) create mode 100644 Tusker/Views/TableViewSwipeActionProvider.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 876dfa93..b0488939 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 99c4f6cd..53b39835 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 c1e785d1..ef6b3d4d 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 4927442e..9f67d775 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 59a5dfdb..792e844e 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 72fdd758..54154fb5 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 00000000..c2554cd9 --- /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? + +}