This repository was archived by the owner on Feb 5, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
V. 1.10.7-beta.1 - Magic Email Clients #178
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
82e7002
Add magic email clients feature
483060a
update version to 1.10.7-beta.1
a59746a
add EmailClients.plist to WordPressAuthenticator bundle, in podspec, …
2b8d9cb
Add unit tests:
fbf6d2b
* Update AppSelector.swift: refactor initializer
2f030b3
Update LinkMailPresenter, use one string with format for the alert me…
c48e1e2
Add sourceView to UIAlertController in AppSelector
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
WordPressAuthenticator/Email Client Picker/AppSelector.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import MessageUI | ||
| import UIKit | ||
|
|
||
| /// App selector that selects an app from a list and opens it | ||
| /// Note: it's a wrapper of UIAlertController (which cannot be sublcassed) | ||
| class AppSelector { | ||
| // the action sheet that will contain the list of apps that can be called | ||
| let alertController: UIAlertController | ||
|
|
||
| /// initializes the picker with a dictionary. Initialization will fail if an empty/invalid app list is passed | ||
| /// - Parameters: | ||
| /// - appList: collection of apps to be added to the selector | ||
| /// - defaultAction: default action, if not nil, will be the first element of the list | ||
| /// - sourceView: the sourceView to anchor the action sheet to | ||
| /// - urlHandler: object that handles app URL schemes; defaults to UIApplication.shared | ||
| init?(with appList: [String: String], | ||
| defaultAction: UIAlertAction? = nil, | ||
| sourceView: UIView, | ||
| urlHandler: URLHandler = UIApplication.shared) { | ||
| /// inline method that builds a list of app calls to be inserted in the action sheet | ||
| func makeAlertActions(from appList: [String: String]) -> [UIAlertAction]? { | ||
| guard !appList.isEmpty else { | ||
| return nil | ||
| } | ||
|
|
||
| var actions = [UIAlertAction]() | ||
| for (name, urlString) in appList { | ||
| guard let url = URL(string: urlString), urlHandler.canOpenURL(url) else { | ||
| continue | ||
| } | ||
| actions.append(UIAlertAction(title: AppSelectorTitles(rawValue: name)?.localized ?? name, style: .default) { action in | ||
| urlHandler.open(url, options: [:], completionHandler: nil) | ||
| }) | ||
| } | ||
|
|
||
| guard !actions.isEmpty else { | ||
| return nil | ||
| } | ||
| //sort the apps alphabetically | ||
| actions = actions.sorted { $0.title ?? "" < $1.title ?? "" } | ||
| actions.append(UIAlertAction(title: AppSelectorTitles.cancel.localized, style: .cancel, handler: nil)) | ||
|
|
||
| if let action = defaultAction { | ||
| actions.insert(action, at: 0) | ||
| } | ||
| return actions | ||
| } | ||
|
|
||
| guard let appCalls = makeAlertActions(from: appList) else { | ||
| return nil | ||
| } | ||
|
|
||
| alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) | ||
| alertController.popoverPresentationController?.sourceView = sourceView | ||
| alertController.popoverPresentationController?.sourceRect = sourceView.bounds | ||
| appCalls.forEach { | ||
| alertController.addAction($0) | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /// Initializers for Email Picker | ||
| extension AppSelector { | ||
| /// initializes the picker with a plist file in a specified bundle | ||
| convenience init?(with plistFile: String, | ||
| in bundle: Bundle, | ||
| defaultAction: UIAlertAction? = nil, | ||
| sourceView: UIView) { | ||
|
|
||
| guard let plistPath = bundle.path(forResource: plistFile, ofType: "plist"), | ||
| let availableApps = NSDictionary(contentsOfFile: plistPath) as? [String: String] else { | ||
| return nil | ||
| } | ||
| self.init(with: availableApps, | ||
| defaultAction: defaultAction, | ||
| sourceView: sourceView) | ||
| } | ||
|
|
||
| /// Convenience init for a picker that calls supported email clients apps, defined in EmailClients.plist | ||
| convenience init?(sourceView: UIView) { | ||
| guard let bundlePath = Bundle(for: type(of: self)) | ||
| .path(forResource: "WordPressAuthenticatorResources", ofType: "bundle"), | ||
| let wpAuthenticatorBundle = Bundle(path: bundlePath) else { | ||
| return nil | ||
| } | ||
|
|
||
| let plistFile = "EmailClients" | ||
| var defaultAction: UIAlertAction? | ||
|
|
||
| // if available, prepend apple mail | ||
| if MFMailComposeViewController.canSendMail(), let url = URL(string: "message://") { | ||
| defaultAction = UIAlertAction(title: AppSelectorTitles.appleMail.localized, style: .default) { action in | ||
| UIApplication.shared.open(url) | ||
| } | ||
| } | ||
| self.init(with: plistFile, | ||
| in: wpAuthenticatorBundle, | ||
| defaultAction: defaultAction, | ||
| sourceView: sourceView) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /// Localizable app selector titles | ||
| enum AppSelectorTitles: String { | ||
| case appleMail | ||
| case gmail | ||
| case airmail | ||
| case msOutlook | ||
| case spark | ||
| case yahooMail | ||
| case fastmail | ||
| case cancel | ||
|
|
||
| var localized: String { | ||
| switch self { | ||
| case .appleMail: | ||
| return NSLocalizedString("Mail (Default)", comment: "Option to select the Apple Mail app when logging in with magic links") | ||
| case .gmail: | ||
| return NSLocalizedString("Gmail", comment: "Option to select the Gmail app when logging in with magic links") | ||
| case .airmail: | ||
| return NSLocalizedString("Airmail", comment: "Option to select the Airmail app when logging in with magic links") | ||
| case .msOutlook: | ||
| return NSLocalizedString("Microsoft Outlook", comment: "Option to select the Microsft Outlook app when logging in with magic links") | ||
| case .spark: | ||
| return NSLocalizedString("Spark", comment: "Option to select the Spark email app when logging in with magic links") | ||
| case .yahooMail: | ||
| return NSLocalizedString("Yahoo Mail", comment: "Option to select the Yahoo Mail app when logging in with magic links") | ||
| case .fastmail: | ||
| return NSLocalizedString("Fastmail", comment: "Option to select the Fastmail app when logging in with magic links") | ||
| case .cancel: | ||
| return NSLocalizedString("Cancel", comment: "Option to cancel the email app selection when logging in with magic links") | ||
| } | ||
| } | ||
| } | ||
49 changes: 49 additions & 0 deletions
49
WordPressAuthenticator/Email Client Picker/LinkMailPresenter.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import MessageUI | ||
|
|
||
|
|
||
| /// Email picker presenter | ||
| class LinkMailPresenter { | ||
|
|
||
| private let emailAddress: String | ||
|
|
||
| init(emailAddress: String) { | ||
| self.emailAddress = emailAddress | ||
| } | ||
|
|
||
| /// Presents the available mail clients in an action sheet. If none is available, | ||
| /// Falls back to Apple Mail and opens it. | ||
| /// If not even Apple Mail is available, presents an alert to check your email | ||
| /// - Parameters: | ||
| /// - viewController: the UIViewController that will present the action sheet | ||
| /// - appSelector: the app picker that contains the available clients. Nil if no clients are available | ||
| /// reads the supported email clients from EmailClients.plist | ||
| func presentEmailClients(on viewController: UIViewController, | ||
| appSelector: AppSelector?) { | ||
|
|
||
| guard let picker = appSelector else { | ||
| // fall back to Apple Mail if no other clients are installed | ||
| if MFMailComposeViewController.canSendMail(), let url = URL(string: "message://") { | ||
| UIApplication.shared.open(url) | ||
| } else { | ||
| showAlertToCheckEmail(on: viewController) | ||
| } | ||
| return | ||
| } | ||
| viewController.present(picker.alertController, animated: true) | ||
| } | ||
|
|
||
| private func showAlertToCheckEmail(on viewController: UIViewController) { | ||
| let title = NSLocalizedString("Check your email!", | ||
| comment: "Alert title for check your email during logIn/signUp.") | ||
|
|
||
| let message = String.localizedStringWithFormat(NSLocalizedString("We just emailed a link to %@. Please check your mail app and tap the link to log in.", | ||
| comment: "message to ask a user to check their email for a WordPress.com email"), emailAddress) | ||
|
|
||
| let alertController = UIAlertController(title: title, | ||
| message: message, | ||
| preferredStyle: .alert) | ||
| alertController.addCancelActionWithTitle(NSLocalizedString("OK", | ||
| comment: "Button title. An acknowledgement of the message displayed in a prompt.")) | ||
| viewController.present(alertController, animated: true, completion: nil) | ||
| } | ||
| } |
13 changes: 13 additions & 0 deletions
13
WordPressAuthenticator/Email Client Picker/URLHandler.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| /// Generic type that handles URL Schemes | ||
| protocol URLHandler { | ||
| /// checks if the specified URL can be opened | ||
| func canOpenURL(_ url: URL) -> Bool | ||
| /// opens the specified URL | ||
| func open(_ url: URL, | ||
| options: [UIApplication.OpenExternalURLOptionsKey : Any], | ||
| completionHandler completion: ((Bool) -> Void)?) | ||
| } | ||
|
|
||
| /// conforms UIApplication to URLHandler to allow dependency injection | ||
| extension UIApplication: URLHandler {} | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.