feat: add share action for profile scene

This commit is contained in:
CMK 2021-04-08 18:52:35 +08:00
parent 672db8f3c2
commit 3b03ed63ce
9 changed files with 166 additions and 13 deletions

View File

@ -44,6 +44,8 @@
"sign_up": "Sign Up",
"see_more": "See More",
"preview": "Preview",
"share": "Share",
"share_user": "Share %s",
"open_in_safari": "Open in Safari"
},
"status": {
@ -73,6 +75,7 @@
"pending": "Pending",
"block": "Block",
"block_user": "Block %s",
"block_domain": "Block %s",
"unblock": "Unblock",
"unblock_user": "Unblock %s",
"blocked": "Blocked",

View File

@ -219,6 +219,7 @@
DB71FD5225F8CCAA00512AE1 /* APIService+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */; };
DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; };
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73B48F261F030A002E9E9F /* SafariActivity.swift */; };
DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */; };
DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */; };
DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */; };
@ -595,6 +596,7 @@
DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Status.swift"; sourceTree = "<group>"; };
DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = "<group>"; };
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
DB73B48F261F030A002E9E9F /* SafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariActivity.swift; sourceTree = "<group>"; };
DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = "<group>"; };
DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = "<group>"; };
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusContentCollectionViewCell.swift; sourceTree = "<group>"; };
@ -1142,8 +1144,8 @@
DB084B5125CBC56300F898ED /* CoreDataStack */ = {
isa = PBXGroup;
children = (
DB45FAE225CA7181005A8AC7 /* MastodonUser.swift */,
DB084B5625CBC56C00F898ED /* Status.swift */,
DB45FAE225CA7181005A8AC7 /* MastodonUser.swift */,
DB9D6C3725E508BE0051B173 /* Attachment.swift */,
);
path = CoreDataStack;
@ -1233,6 +1235,7 @@
DB9E0D6925EDFFE500CFDD76 /* Helper */,
DB8AF56225C138BC002E6C99 /* Extension */,
2D5A3D0125CF8640002347D6 /* Vender */,
DB73B495261F030D002E9E9F /* Activity */,
DB5086CB25CC0DB400C2C187 /* Preference */,
2D69CFF225CA9E2200C3A1B2 /* Protocol */,
DB98338425C945ED00AD9700 /* Generated */,
@ -1372,6 +1375,14 @@
path = ServerRules;
sourceTree = "<group>";
};
DB73B495261F030D002E9E9F /* Activity */ = {
isa = PBXGroup;
children = (
DB73B48F261F030A002E9E9F /* SafariActivity.swift */,
);
path = Activity;
sourceTree = "<group>";
};
DB789A1025F9F29B0071ACA0 /* Compose */ = {
isa = PBXGroup;
children = (
@ -2219,6 +2230,7 @@
5DDDF1A92617489F00311060 /* Mastodon+Entity+History.swift in Sources */,
DB59F11825EFA35B001F1DAB /* StripProgressView.swift in Sources */,
DB59F10425EF5EBC001F1DAB /* TableViewCellHeightCacheableContainer.swift in Sources */,
DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */,
DB9A48962603685D008B817C /* MastodonAttachmentService+UploadState.swift in Sources */,
DB66728C25F9F8DC00D60309 /* ComposeViewModel+Diffable.swift in Sources */,
DBE0822425CD3F1E00FD6BBD /* MastodonRegisterViewModel.swift in Sources */,

View File

@ -0,0 +1,62 @@
//
// SafariActivity.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-4-8.
//
import UIKit
import SafariServices
final class SafariActivity: UIActivity {
weak var sceneCoordinator: SceneCoordinator?
var url: NSURL?
init(sceneCoordinator: SceneCoordinator) {
self.sceneCoordinator = sceneCoordinator
}
override var activityType: UIActivity.ActivityType? {
return UIActivity.ActivityType("org.joinmastodon.Mastodon.safari-activity")
}
override var activityTitle: String? {
return L10n.Common.Controls.Actions.openInSafari
}
override var activityImage: UIImage? {
return UIImage(systemName: "safari")
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
for item in activityItems {
guard let _ = item as? NSURL, sceneCoordinator != nil else { continue }
return true
}
return false
}
override func prepare(withActivityItems activityItems: [Any]) {
for item in activityItems {
guard let url = item as? NSURL else { continue }
self.url = url
}
}
override var activityViewController: UIViewController? {
return nil
}
override func perform() {
guard let url = url else {
activityDidFinish(false)
return
}
sceneCoordinator?.present(scene: .safari(url: url as URL), from: nil, transition: .safariPresent(animated: true, completion: nil))
activityDidFinish(true)
}
}

View File

@ -33,8 +33,8 @@ extension SceneCoordinator {
case custom(transitioningDelegate: UIViewControllerTransitioningDelegate)
case customPush
case safariPresent(animated: Bool, completion: (() -> Void)? = nil)
case activityViewControllerPresent(animated: Bool, completion: (() -> Void)? = nil)
case alertController(animated: Bool, completion: (() -> Void)? = nil)
case activityViewControllerPresent(animated: Bool, completion: (() -> Void)? = nil)
}
enum Scene {
@ -59,8 +59,9 @@ extension SceneCoordinator {
case favorite(viewModel: FavoriteViewModel)
// misc
case alertController(alertController: UIAlertController)
case safari(url: URL)
case alertController(alertController: UIAlertController)
case activityViewController(activityViewController: UIActivityViewController, sourceView: UIView?, barButtonItem: UIBarButtonItem?)
#if DEBUG
case publicTimeline
@ -170,11 +171,11 @@ extension SceneCoordinator {
viewController.modalPresentationCapturesStatusBarAppearance = true
presentingViewController.present(viewController, animated: animated, completion: completion)
case .activityViewControllerPresent(let animated, let completion):
case .alertController(let animated, let completion):
viewController.modalPresentationCapturesStatusBarAppearance = true
presentingViewController.present(viewController, animated: animated, completion: completion)
case .alertController(let animated, let completion):
case .activityViewControllerPresent(let animated, let completion):
viewController.modalPresentationCapturesStatusBarAppearance = true
presentingViewController.present(viewController, animated: animated, completion: completion)
}
@ -237,6 +238,12 @@ private extension SceneCoordinator {
let _viewController = FavoriteViewController()
_viewController.viewModel = viewModel
viewController = _viewController
case .safari(let url):
guard let scheme = url.scheme?.lowercased(),
scheme == "http" || scheme == "https" else {
return nil
}
viewController = SFSafariViewController(url: url)
case .alertController(let alertController):
if let popoverPresentationController = alertController.popoverPresentationController {
assert(
@ -246,12 +253,10 @@ private extension SceneCoordinator {
)
}
viewController = alertController
case .safari(let url):
guard let scheme = url.scheme?.lowercased(),
scheme == "http" || scheme == "https" else {
return nil
}
viewController = SFSafariViewController(url: url)
case .activityViewController(let activityViewController, let sourceView, let barButtonItem):
activityViewController.popoverPresentationController?.sourceView = sourceView
activityViewController.popoverPresentationController?.barButtonItem = barButtonItem
viewController = activityViewController
#if DEBUG
case .publicTimeline:
let _viewController = PublicTimelineViewController()

View File

@ -63,3 +63,21 @@ extension MastodonUser {
}
}
extension MastodonUser {
var profileURL: URL {
if let urlString = self.url,
let url = URL(string: urlString) {
return url
} else {
return URL(string: "https://\(self.domain)/@\(username)")!
}
}
var activityItems: [Any] {
var items: [Any] = []
items.append(profileURL)
return items
}
}

View File

@ -78,6 +78,12 @@ internal enum L10n {
internal static let savePhoto = L10n.tr("Localizable", "Common.Controls.Actions.SavePhoto")
/// See More
internal static let seeMore = L10n.tr("Localizable", "Common.Controls.Actions.SeeMore")
/// Share
internal static let share = L10n.tr("Localizable", "Common.Controls.Actions.Share")
/// Share %@
internal static func shareUser(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Actions.ShareUser", String(describing: p1))
}
/// Sign In
internal static let signIn = L10n.tr("Localizable", "Common.Controls.Actions.SignIn")
/// Sign Up
@ -90,6 +96,10 @@ internal enum L10n {
internal enum Firendship {
/// Block
internal static let block = L10n.tr("Localizable", "Common.Controls.Firendship.Block")
/// Block %@
internal static func blockDomain(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Firendship.BlockDomain", String(describing: p1))
}
/// Blocked
internal static let blocked = L10n.tr("Localizable", "Common.Controls.Firendship.Blocked")
/// Block %@

View File

@ -139,7 +139,10 @@ extension UserProviderFacade {
for mastodonUser: MastodonUser,
isMuting: Bool,
isBlocking: Bool,
provider: UserProvider
needsShareAction: Bool,
provider: UserProvider,
sourceView: UIView?,
barButtonItem: UIBarButtonItem?
) -> UIMenu {
var children: [UIMenuElement] = []
let name = mastodonUser.displayNameWithFallback
@ -198,7 +201,32 @@ extension UserProviderFacade {
children.append(blockMenu)
}
if needsShareAction {
let shareAction = UIAction(title: L10n.Common.Controls.Actions.shareUser(name), image: UIImage(systemName: "square.and.arrow.up"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in
guard let provider = provider else { return }
let activityViewController = createActivityViewControllerForMastodonUser(mastodonUser: mastodonUser, dependency: provider)
provider.coordinator.present(
scene: .activityViewController(
activityViewController: activityViewController,
sourceView: sourceView,
barButtonItem: barButtonItem
),
from: provider,
transition: .activityViewControllerPresent(animated: true, completion: nil)
)
}
children.append(shareAction)
}
return UIMenu(title: "", options: [], children: children)
}
static func createActivityViewControllerForMastodonUser(mastodonUser: MastodonUser, dependency: NeedsDependency) -> UIActivityViewController {
let activityViewController = UIActivityViewController(
activityItems: mastodonUser.activityItems,
applicationActivities: [SafariActivity(sceneCoordinator: dependency.coordinator)]
)
return activityViewController
}
}

View File

@ -24,11 +24,14 @@ Please check your internet connection.";
"Common.Controls.Actions.Save" = "Save";
"Common.Controls.Actions.SavePhoto" = "Save photo";
"Common.Controls.Actions.SeeMore" = "See More";
"Common.Controls.Actions.Share" = "Share";
"Common.Controls.Actions.ShareUser" = "Share %@";
"Common.Controls.Actions.SignIn" = "Sign In";
"Common.Controls.Actions.SignUp" = "Sign Up";
"Common.Controls.Actions.TakePhoto" = "Take photo";
"Common.Controls.Actions.TryAgain" = "Try Again";
"Common.Controls.Firendship.Block" = "Block";
"Common.Controls.Firendship.BlockDomain" = "Block %@";
"Common.Controls.Firendship.BlockUser" = "Block %@";
"Common.Controls.Firendship.Blocked" = "Blocked";
"Common.Controls.Firendship.EditInfo" = "Edit info";

View File

@ -325,7 +325,8 @@ extension ProfileViewController {
}
let isMuting = relationshipActionOptionSet.contains(.muting)
let isBlocking = relationshipActionOptionSet.contains(.blocking)
self.moreMenuBarButtonItem.menu = UserProviderFacade.createProfileActionMenu(for: mastodonUser, isMuting: isMuting, isBlocking: isBlocking, provider: self)
let needsShareAction = self.viewModel.isMeBarButtonItemsHidden.value
self.moreMenuBarButtonItem.menu = UserProviderFacade.createProfileActionMenu(for: mastodonUser, isMuting: isMuting, isBlocking: isBlocking, needsShareAction: needsShareAction, provider: self, sourceView: nil, barButtonItem: self.moreMenuBarButtonItem)
}
.store(in: &disposeBag)
viewModel.isRelationshipActionButtonHidden
@ -446,6 +447,17 @@ extension ProfileViewController {
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
guard let mastodonUser = viewModel.mastodonUser.value else { return }
let activityViewController = UserProviderFacade.createActivityViewControllerForMastodonUser(mastodonUser: mastodonUser, dependency: self)
coordinator.present(
scene: .activityViewController(
activityViewController: activityViewController,
sourceView: nil,
barButtonItem: sender
),
from: self,
transition: .activityViewControllerPresent(animated: true, completion: nil)
)
}
@objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) {