From b60688f4b86238a49bf35b3d387904da4c6f5e49 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 5 Sep 2019 12:49:17 -0400 Subject: [PATCH] Show share sheet with custom activities instead of alert controller for account actions --- Tusker.xcodeproj/project.pbxproj | 40 ++++++++++++++ .../Account Activities/AccountActivity.swift | 34 ++++++++++++ .../FollowAccountActivity.swift | 42 ++++++++++++++ .../SendMesasgeActivity.swift | 34 ++++++++++++ .../UnfollowAccountActivity.swift | 42 ++++++++++++++ Tusker/Activities/OpenInSafariActivity.swift | 47 ++++++++++++++++ Tusker/Activities/UIActivity+Types.swift | 18 ++++++ .../Profile/ProfileTableViewController.swift | 55 +++++++------------ 8 files changed, 277 insertions(+), 35 deletions(-) create mode 100644 Tusker/Activities/Account Activities/AccountActivity.swift create mode 100644 Tusker/Activities/Account Activities/FollowAccountActivity.swift create mode 100644 Tusker/Activities/Account Activities/SendMesasgeActivity.swift create mode 100644 Tusker/Activities/Account Activities/UnfollowAccountActivity.swift create mode 100644 Tusker/Activities/OpenInSafariActivity.swift create mode 100644 Tusker/Activities/UIActivity+Types.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index fb294841..1b7bdb85 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -148,6 +148,12 @@ D68632AC21ED8319008C716E /* GMImagePicker.strings in Resources */ = {isa = PBXBuildFile; fileRef = D686329321ED8319008C716E /* GMImagePicker.strings */; }; D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; }; D6A5FAFB217B86CE003DB2D9 /* OnboardingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */; }; + D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; }; + D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; }; + D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; }; + D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB4423216AF800E5038B /* FollowAccountActivity.swift */; }; + D6AEBB4823216B1D00E5038B /* AccountActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB4723216B1D00E5038B /* AccountActivity.swift */; }; + D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB4923216F0400E5038B /* UnfollowAccountActivity.swift */; }; D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; }; D6BC874521961F73006163F1 /* Gifu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; }; D6BC874621961F73006163F1 /* Gifu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D6BC874421961F73006163F1 /* Gifu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -382,6 +388,12 @@ D686329421ED8319008C716E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = GMImagePicker.strings; sourceTree = ""; }; D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = ""; }; D6A5FAFA217B86CE003DB2D9 /* OnboardingViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OnboardingViewController.xib; sourceTree = ""; }; + D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = ""; }; + D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = ""; }; + D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = ""; }; + D6AEBB4423216AF800E5038B /* FollowAccountActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowAccountActivity.swift; sourceTree = ""; }; + D6AEBB4723216B1D00E5038B /* AccountActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountActivity.swift; sourceTree = ""; }; + D6AEBB4923216F0400E5038B /* UnfollowAccountActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnfollowAccountActivity.swift; sourceTree = ""; }; D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = ""; }; D6BC874421961F73006163F1 /* Gifu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Gifu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedTableViewController.swift; sourceTree = ""; }; @@ -918,6 +930,27 @@ path = de.lproj; sourceTree = ""; }; + D6AEBB3F2321640F00E5038B /* Activities */ = { + isa = PBXGroup; + children = ( + D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */, + D6AEBB4623216B0C00E5038B /* Account Activities */, + D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */, + ); + path = Activities; + sourceTree = ""; + }; + D6AEBB4623216B0C00E5038B /* Account Activities */ = { + isa = PBXGroup; + children = ( + D6AEBB4723216B1D00E5038B /* AccountActivity.swift */, + D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */, + D6AEBB4423216AF800E5038B /* FollowAccountActivity.swift */, + D6AEBB4923216F0400E5038B /* UnfollowAccountActivity.swift */, + ); + path = "Account Activities"; + sourceTree = ""; + }; D6BED1722126661300F02DA0 /* Views */ = { isa = PBXGroup; children = ( @@ -1001,6 +1034,7 @@ D6757A7A2157E00100721E32 /* XCallbackURL */, D62D241E217AA46B005076CC /* Shortcuts */, D663626021360A9600C9CBA2 /* Preferences */, + D6AEBB3F2321640F00E5038B /* Activities */, D667E5F62135C2ED0057A976 /* Extensions */, D6F953F121251A2F00CF0F2B /* Controllers */, D641C780213DD7C4004B4513 /* Screens */, @@ -1403,6 +1437,7 @@ 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */, D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */, 04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */, + D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */, D6C693F92162E4DB007D6A6D /* StatusContentLabel.swift in Sources */, D6D58DF922074B74009C8DD9 /* LinkLabel.swift in Sources */, 0454DDAF22B462EF00B8BB8E /* GalleryExpandAnimationController.swift in Sources */, @@ -1423,12 +1458,15 @@ D67C57B421E2910700C3118B /* ComposeStatusReplyView.swift in Sources */, 04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */, D627FF7B217E951500CC0648 /* DraftsTableViewController.swift in Sources */, + D6AEBB4823216B1D00E5038B /* AccountActivity.swift in Sources */, D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */, 04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */, D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */, + D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */, D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */, D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */, D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */, + D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */, D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */, D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */, D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */, @@ -1448,6 +1486,7 @@ D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */, D67C57AD21E265FC00C3118B /* LargeAccountDetailView.swift in Sources */, D641C777213CAA9E004B4513 /* ActionNotificationTableViewCell.swift in Sources */, + D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */, D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */, D64D0AB12128D9AE005A6F37 /* OnboardingViewController.swift in Sources */, D627FF76217E923E00CC0648 /* DraftsManager.swift in Sources */, @@ -1466,6 +1505,7 @@ D6757A7C2157E01900721E32 /* XCBManager.swift in Sources */, D6F1F84D2193B56E00F5FE67 /* Cache.swift in Sources */, 0427037C22B316B9000D31B6 /* SilentActionPrefs.swift in Sources */, + D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */, D6757A7E2157E02600721E32 /* XCBRequestSpec.swift in Sources */, D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */, D6BC8748219738E1006163F1 /* EnhancedTableViewController.swift in Sources */, diff --git a/Tusker/Activities/Account Activities/AccountActivity.swift b/Tusker/Activities/Account Activities/AccountActivity.swift new file mode 100644 index 00000000..e12e2233 --- /dev/null +++ b/Tusker/Activities/Account Activities/AccountActivity.swift @@ -0,0 +1,34 @@ +// +// AccountActivity.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +class AccountActivity: UIActivity { + + override class var activityCategory: UIActivity.Category { + return .action + } + + var account: Account? + + override func canPerform(withActivityItems activityItems: [Any]) -> Bool { + for case is Account in activityItems { + return true + } + return false + } + + override func prepare(withActivityItems activityItems: [Any]) { + for case let account as Account in activityItems { + self.account = account + return + } + } + +} diff --git a/Tusker/Activities/Account Activities/FollowAccountActivity.swift b/Tusker/Activities/Account Activities/FollowAccountActivity.swift new file mode 100644 index 00000000..6747ec0f --- /dev/null +++ b/Tusker/Activities/Account Activities/FollowAccountActivity.swift @@ -0,0 +1,42 @@ +// +// FollowAccountActivity.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +class FollowAccountActivity: AccountActivity { + + override var activityType: UIActivity.ActivityType? { + return .followAccount + } + + override var activityTitle: String? { + return NSLocalizedString("Follow", comment: "follow account activity title") + } + override var activityImage: UIImage? { + return UIImage(systemName: "person.badge.plus") + } + + override func perform() { + guard let account = account else { return } + + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + + let request = Account.follow(account.id) + MastodonController.client.run(request) { (response) in + if case let .success(relationship, _) = response { + MastodonCache.add(relationship: relationship) + } else { + // todo: display error message + UINotificationFeedbackGenerator().notificationOccurred(.error) + fatalError() + } + } + } + +} diff --git a/Tusker/Activities/Account Activities/SendMesasgeActivity.swift b/Tusker/Activities/Account Activities/SendMesasgeActivity.swift new file mode 100644 index 00000000..0238b0ab --- /dev/null +++ b/Tusker/Activities/Account Activities/SendMesasgeActivity.swift @@ -0,0 +1,34 @@ +// +// SendMesasgeActivity.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit + +class SendMessageActivity: AccountActivity { + + override var activityType: UIActivity.ActivityType? { + return .sendMessageMentioningAccount + } + + override var activityTitle: String? { + return NSLocalizedString("Send Message", comment: "send message activity title") + } + override var activityImage: UIImage? { + return UIImage(systemName: "square.and.pencil") + } + + override func perform() { + activityDidFinish(true) + } + + override var activityViewController: UIViewController? { + guard let account = account else { return nil } + + return UINavigationController(rootViewController: ComposeViewController(mentioningAcct: account.acct)) + } + +} diff --git a/Tusker/Activities/Account Activities/UnfollowAccountActivity.swift b/Tusker/Activities/Account Activities/UnfollowAccountActivity.swift new file mode 100644 index 00000000..303042a5 --- /dev/null +++ b/Tusker/Activities/Account Activities/UnfollowAccountActivity.swift @@ -0,0 +1,42 @@ +// +// UnfollowActivity.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit +import Pachyderm + +class UnfollowAccountActivity: AccountActivity { + + override var activityType: UIActivity.ActivityType? { + return .unfollowAccount + } + + override var activityTitle: String? { + return NSLocalizedString("Unfollow", comment: "unfollow account activity title") + } + override var activityImage: UIImage? { + return UIImage(systemName: "person.badge.minus") + } + + override func perform() { + guard let account = account else { return } + + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + + let request = Account.unfollow(account.id) + MastodonController.client.run(request) { (response) in + if case let .success(relationship, _) = response { + MastodonCache.add(relationship: relationship) + } else { + // todo: display error message + UINotificationFeedbackGenerator().notificationOccurred(.error) + fatalError() + } + } + } + +} diff --git a/Tusker/Activities/OpenInSafariActivity.swift b/Tusker/Activities/OpenInSafariActivity.swift new file mode 100644 index 00000000..848bc0c9 --- /dev/null +++ b/Tusker/Activities/OpenInSafariActivity.swift @@ -0,0 +1,47 @@ +// +// OpenInSafariActivity.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit +import SafariServices + +class OpenInSafariActivity: UIActivity { + + override class var activityCategory: UIActivity.Category { + return .action + } + override var activityType: UIActivity.ActivityType? { + return .openInSafari + } + + override var activityTitle: String? { + return NSLocalizedString("Open in Safari", comment: "open in safari activity title") + } + override var activityImage: UIImage? { + return UIImage(systemName: "safari") + } + + override func canPerform(withActivityItems activityItems: [Any]) -> Bool { + for case is URL in activityItems { + return true + } + return false + } + + override func perform() { + activityDidFinish(true) + } + + static func completionHandler(viewController: UIViewController, url: URL) -> UIActivityViewController.CompletionWithItemsHandler { + return { (activityType, _, _, _) in + if activityType == .openInSafari { + viewController.present(SFSafariViewController(url: url), animated: true) + } + } + } + +} diff --git a/Tusker/Activities/UIActivity+Types.swift b/Tusker/Activities/UIActivity+Types.swift new file mode 100644 index 00000000..1f5d9a18 --- /dev/null +++ b/Tusker/Activities/UIActivity+Types.swift @@ -0,0 +1,18 @@ +// +// UIActivity+Types.swift +// Tusker +// +// Created by Shadowfacts on 9/5/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit + +extension UIActivity.ActivityType { + + static let openInSafari = UIActivity.ActivityType("\(Bundle.main.bundleIdentifier!).open_in_safari") + static let sendMessageMentioningAccount = UIActivity.ActivityType("\(Bundle.main.bundleIdentifier!).send_message_mentioning_account") + static let followAccount = UIActivity.ActivityType("\(Bundle.main.bundleIdentifier!).follow_account") + static let unfollowAccount = UIActivity.ActivityType("\(Bundle.main.bundleIdentifier!).unfollow_account") + +} diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index 24f10c53..29414518 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -230,43 +230,28 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { func showMoreOptions() { let account = MastodonCache.account(for: accountID)! - let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { _ in - let vc = SFSafariViewController(url: account.url) - self.present(vc, animated: true) - })) - alert.addAction(UIAlertAction(title: "Share", style: .default, handler: { _ in - let vc = UIActivityViewController(activityItems: [account.url], applicationActivities: nil) - self.present(vc, animated: true) - })) - alert.addAction(UIAlertAction(title: "Send Message", style: .default, handler: { _ in - self.sendMessageMentioning() - })) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - - MastodonCache.relationship(for: account.id) { (relationship) in - guard let relationship = relationship else { - DispatchQueue.main.async { - self.present(alert, animated: true) - } - return + MastodonCache.relationship(for: account.id) { [weak self] (relationship) in + guard let self = self else { return } + + let customActivities: [UIActivity] + if let relationship = relationship { + let toggleFollowActivity = relationship.following ? UnfollowAccountActivity() : FollowAccountActivity() + customActivities = [ + SendMessageActivity(), + toggleFollowActivity, + OpenInSafariActivity() + ] + } else { + customActivities = [ + SendMessageActivity(), + OpenInSafariActivity() + ] } - let title = relationship.following ? "Unfollow": "Follow" + DispatchQueue.main.async { - alert.addAction(UIAlertAction(title: title, style: .default, handler: { (_) in - UIImpactFeedbackGenerator(style: .medium).impactOccurred() - let request = (relationship.following ? Account.unfollow : Account.follow)(account.id) - MastodonController.client.run(request, completion: { (response) in - if case let .success(relationship, _) = response { - MastodonCache.add(relationship: relationship) - } else { - // todo: display error message - UINotificationFeedbackGenerator().notificationOccurred(.error) - fatalError() - } - }) - })) - self.present(alert, animated: true) + let activityController = UIActivityViewController(activityItems: [account.url, account], applicationActivities: customActivities) + activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(viewController: self, url: account.url) + self.present(activityController, animated: true) } } }