2017-07-13 20:53:24 +02:00
//
2018-02-23 18:46:04 +01:00
// C o p y r i g h t ( c ) 2 0 1 8 O p e n W h i s p e r S y s t e m s . A l l r i g h t s r e s e r v e d .
2017-07-13 20:53:24 +02:00
//
2016-11-18 23:11:56 +01:00
import Foundation
import Social
import ContactsUI
import MessageUI
2017-11-28 00:17:46 +01:00
import SignalServiceKit
2016-11-18 23:11:56 +01:00
@objc ( OWSInviteFlow )
2016-11-23 17:07:38 +01:00
class InviteFlow : NSObject , MFMessageComposeViewControllerDelegate , MFMailComposeViewControllerDelegate , ContactsPickerDelegate {
2016-11-18 23:11:56 +01:00
enum Channel {
case message , mail , twitter
}
let TAG = " [ShareActions] "
2016-11-23 00:02:14 +01:00
let installUrl = " https://signal.org/install/ "
2017-10-14 18:18:10 +02:00
let homepageUrl = " https://signal.org "
2016-11-18 23:11:56 +01:00
2018-05-25 23:17:15 +02:00
@objc
2016-11-18 23:11:56 +01:00
let actionSheetController : UIAlertController
2018-05-25 23:17:15 +02:00
@objc
2016-11-18 23:11:56 +01:00
let presentingViewController : UIViewController
2016-11-23 17:07:38 +01:00
let contactsManager : OWSContactsManager
2016-11-18 23:11:56 +01:00
var channel : Channel ?
2018-05-25 23:17:15 +02:00
@objc
2016-11-23 17:07:38 +01:00
required init ( presentingViewController : UIViewController , contactsManager : OWSContactsManager ) {
2016-11-18 23:11:56 +01:00
self . presentingViewController = presentingViewController
2016-11-23 17:07:38 +01:00
self . contactsManager = contactsManager
2016-11-18 23:11:56 +01:00
actionSheetController = UIAlertController ( title : nil , message : nil , preferredStyle : . actionSheet )
super . init ( )
actionSheetController . addAction ( dismissAction ( ) )
2017-07-13 20:53:24 +02:00
2018-02-23 18:46:04 +01:00
if let messageAction = messageAction ( ) {
actionSheetController . addAction ( messageAction )
}
2016-11-18 23:11:56 +01:00
2018-02-23 18:46:04 +01:00
if let mailAction = mailAction ( ) {
actionSheetController . addAction ( mailAction )
2016-11-18 23:11:56 +01:00
}
if let tweetAction = tweetAction ( ) {
actionSheetController . addAction ( tweetAction )
}
}
2018-05-25 20:27:23 +02:00
deinit {
Logger . verbose ( " [InviteFlow] deinit " )
}
2016-11-18 23:11:56 +01:00
// MARK: T w i t t e r
func canTweet ( ) -> Bool {
return SLComposeViewController . isAvailable ( forServiceType : SLServiceTypeTwitter )
}
func tweetAction ( ) -> UIAlertAction ? {
guard canTweet ( ) else {
Logger . info ( " \( TAG ) Twitter not supported. " )
return nil
}
guard let twitterViewController = SLComposeViewController ( forServiceType : SLServiceTypeTwitter ) else {
Logger . error ( " \( TAG ) unable to build twitter controller. " )
return nil
}
2018-02-23 18:46:04 +01:00
let tweetString = NSLocalizedString ( " SETTINGS_INVITE_TWITTER_TEXT " , comment : " content of tweet when inviting via twitter " )
2016-11-18 23:11:56 +01:00
twitterViewController . setInitialText ( tweetString )
2016-11-23 00:02:14 +01:00
let tweetUrl = URL ( string : installUrl )
2016-11-18 23:11:56 +01:00
twitterViewController . add ( tweetUrl )
2016-11-23 17:07:38 +01:00
twitterViewController . add ( # imageLiteral ( resourceName : " twitter_sharing_image " ) )
2016-11-18 23:11:56 +01:00
2018-02-23 18:46:04 +01:00
let tweetTitle = NSLocalizedString ( " SHARE_ACTION_TWEET " , comment : " action sheet item " )
2017-07-13 20:53:24 +02:00
return UIAlertAction ( title : tweetTitle , style : . default ) { _ in
2016-11-18 23:11:56 +01:00
Logger . debug ( " \( self . TAG ) Chose tweet " )
self . presentingViewController . present ( twitterViewController , animated : true , completion : nil )
}
}
func dismissAction ( ) -> UIAlertAction {
2017-07-13 20:53:24 +02:00
return UIAlertAction ( title : CommonStrings . dismissButton , style : . cancel )
2016-11-18 23:11:56 +01:00
}
2016-11-23 17:07:38 +01:00
// MARK: C o n t a c t s P i c k e r D e l e g a t e
2016-11-18 23:11:56 +01:00
2016-11-23 17:07:38 +01:00
func contactsPicker ( _ : ContactsPicker , didSelectMultipleContacts contacts : [ Contact ] ) {
2016-11-18 23:11:56 +01:00
Logger . debug ( " \( TAG ) didSelectContacts: \( contacts ) " )
guard let inviteChannel = channel else {
Logger . error ( " \( TAG ) unexpected nil channel after returning from contact picker. " )
2018-05-25 20:27:23 +02:00
self . presentingViewController . dismiss ( animated : true )
2016-11-18 23:11:56 +01:00
return
}
switch inviteChannel {
case . message :
2016-11-23 17:07:38 +01:00
let phoneNumbers : [ String ] = contacts . map { $0 . userTextPhoneNumbers . first } . filter { $0 != nil } . map { $0 ! }
2017-10-14 18:18:10 +02:00
dismissAndSendSMSTo ( phoneNumbers : phoneNumbers )
2016-11-18 23:11:56 +01:00
case . mail :
2016-11-23 17:07:38 +01:00
let recipients : [ String ] = contacts . map { $0 . emails . first } . filter { $0 != nil } . map { $0 ! }
sendMailTo ( emails : recipients )
2016-11-18 23:11:56 +01:00
default :
Logger . error ( " \( TAG ) unexpected channel after returning from contact picker: \( inviteChannel ) " )
}
}
2016-11-23 17:07:38 +01:00
func contactsPicker ( _ : ContactsPicker , shouldSelectContact contact : Contact ) -> Bool {
guard let inviteChannel = channel else {
Logger . error ( " \( TAG ) unexpected nil channel in contact picker. " )
return true
}
switch inviteChannel {
case . message :
return contact . userTextPhoneNumbers . count > 0
case . mail :
return contact . emails . count > 0
default :
Logger . error ( " \( TAG ) unexpected channel after returning from contact picker: \( inviteChannel ) " )
}
return true
2016-11-18 23:11:56 +01:00
}
2018-05-01 22:38:54 +02:00
func contactsPicker ( _ : ContactsPicker , contactFetchDidFail error : NSError ) {
Logger . error ( " \( self . logTag ) in \( #function ) with error: \( error ) " )
2018-05-25 20:30:54 +02:00
self . presentingViewController . dismiss ( animated : true ) {
OWSAlerts . showErrorAlert ( message : NSLocalizedString ( " ERROR_COULD_NOT_FETCH_CONTACTS " , comment : " Error indicating that the phone's contacts could not be retrieved. " ) )
}
2018-05-01 22:38:54 +02:00
}
func contactsPickerDidCancel ( _ : ContactsPicker ) {
Logger . debug ( " \( self . logTag ) in \( #function ) " )
2018-05-09 17:54:10 +02:00
self . presentingViewController . dismiss ( animated : true )
2018-05-01 22:38:54 +02:00
}
func contactsPicker ( _ : ContactsPicker , didSelectContact contact : Contact ) {
owsFail ( " \( logTag ) in \( #function ) InviteFlow only supports multi-select " )
2018-05-09 17:54:10 +02:00
self . presentingViewController . dismiss ( animated : true )
2018-05-01 22:38:54 +02:00
}
2016-11-18 23:11:56 +01:00
// MARK: S M S
func messageAction ( ) -> UIAlertAction ? {
guard MFMessageComposeViewController . canSendText ( ) else {
Logger . info ( " \( TAG ) Device cannot send text " )
return nil
}
let messageTitle = NSLocalizedString ( " SHARE_ACTION_MESSAGE " , comment : " action sheet item to open native messages app " )
2017-07-13 20:53:24 +02:00
return UIAlertAction ( title : messageTitle , style : . default ) { _ in
2016-11-18 23:11:56 +01:00
Logger . debug ( " \( self . TAG ) Chose message. " )
self . channel = . message
2018-05-09 17:54:10 +02:00
let picker = ContactsPicker ( allowsMultipleSelection : true , subtitleCellType : . phoneNumber )
picker . contactsPickerDelegate = self
2018-05-01 22:38:54 +02:00
picker . title = NSLocalizedString ( " INVITE_FRIENDS_PICKER_TITLE " , comment : " Navbar title " )
2018-05-17 04:42:00 +02:00
let navigationController = OWSNavigationController ( rootViewController : picker )
2016-11-23 17:07:38 +01:00
self . presentingViewController . present ( navigationController , animated : true )
2016-11-18 23:11:56 +01:00
}
}
2017-10-14 18:18:10 +02:00
public func dismissAndSendSMSTo ( phoneNumbers : [ String ] ) {
2016-11-18 23:11:56 +01:00
self . presentingViewController . dismiss ( animated : true ) {
2018-06-01 16:43:43 +02:00
if phoneNumbers . count > 1 {
2018-05-31 19:46:59 +02:00
let warning = UIAlertController ( title : nil ,
message : NSLocalizedString ( " INVITE_WARNING_MULTIPLE_INVITES_BY_TEXT " ,
comment : " Alert warning that sending an invite to multiple users will create a group message whose recipients will be able to see each other. " ) ,
preferredStyle : . alert )
warning . addAction ( UIAlertAction ( title : NSLocalizedString ( " BUTTON_CONTINUE " ,
comment : " Label for 'continue' button. " ) ,
style : . default , handler : { _ in
self . sendSMSTo ( phoneNumbers : phoneNumbers )
} ) )
warning . addAction ( OWSAlerts . cancelAction )
self . presentingViewController . present ( warning , animated : true , completion : nil )
} else {
self . sendSMSTo ( phoneNumbers : phoneNumbers )
}
2017-10-14 18:18:10 +02:00
}
}
2016-11-18 23:11:56 +01:00
2018-05-25 23:17:15 +02:00
@objc
2017-10-14 18:18:10 +02:00
public func sendSMSTo ( phoneNumbers : [ String ] ) {
if #available ( iOS 10.0 , * ) {
// i O S 1 0 m e s s a g e c o m p o s e v i e w d o e s n ' t r e s p e c t s o m e s y s t e m a p p e a r e n c e a t t r i b u t e s .
// S p e c i f i c a l l y , t h e t i t l e i s w h i t e , b u t t h e n a v b a r i s g r a y .
// S o , w e h a v e t o s e t s y s t e m a p p e a r e n c e b e f o r e i n i t ' i n g t h e m e s s a g e c o m p o s e v i e w c o n t r o l l e r i n o r d e r
// t o m a k e i t s c o l o r s l e g i b l e .
// T h e n w e h a v e t o b e s u r e t o s e t i t b a c k i n t h e C o m p o s e V i e w C o n t r o l l e r D e l e g a t e c a l l b a c k .
UIUtil . applyDefaultSystemAppearence ( )
2016-11-18 23:11:56 +01:00
}
2017-10-14 18:18:10 +02:00
let messageComposeViewController = MFMessageComposeViewController ( )
messageComposeViewController . messageComposeDelegate = self
messageComposeViewController . recipients = phoneNumbers
2018-02-23 18:46:04 +01:00
let inviteText = NSLocalizedString ( " SMS_INVITE_BODY " , comment : " body sent to contacts when inviting to Install Signal " )
2017-10-14 18:18:10 +02:00
messageComposeViewController . body = inviteText . appending ( " \( self . installUrl ) " )
2018-05-25 23:11:08 +02:00
self . presentingViewController . present ( messageComposeViewController , animated : true )
2016-11-18 23:11:56 +01:00
}
// MARK: M e s s a g e C o m p o s e V i e w C o n t r o l l e r D e l e g a t e
func messageComposeViewController ( _ controller : MFMessageComposeViewController , didFinishWith result : MessageComposeResult ) {
// R e v e r t s y s t e m s t y l i n g a p p l i e d t o m a k e m e s s a g i n g a p p l e g i b l e o n i O S 1 0 .
UIUtil . applySignalAppearence ( )
2018-05-25 20:27:23 +02:00
self . presentingViewController . dismiss ( animated : true ) {
switch result {
case . failed :
let warning = UIAlertController ( title : nil , message : NSLocalizedString ( " SEND_INVITE_FAILURE " , comment : " Alert body after invite failed " ) , preferredStyle : . alert )
warning . addAction ( UIAlertAction ( title : CommonStrings . dismissButton , style : . default , handler : nil ) )
self . presentingViewController . present ( warning , animated : true , completion : nil )
case . sent :
Logger . debug ( " \( self . TAG ) user successfully invited their friends via SMS. " )
case . cancelled :
Logger . debug ( " \( self . TAG ) user cancelled message invite " )
}
2016-11-18 23:11:56 +01:00
}
}
// MARK: M a i l
func mailAction ( ) -> UIAlertAction ? {
guard MFMailComposeViewController . canSendMail ( ) else {
Logger . info ( " \( TAG ) Device cannot send mail " )
return nil
}
let mailActionTitle = NSLocalizedString ( " SHARE_ACTION_MAIL " , comment : " action sheet item to open native mail app " )
2017-07-13 20:53:24 +02:00
return UIAlertAction ( title : mailActionTitle , style : . default ) { _ in
2016-11-18 23:11:56 +01:00
Logger . debug ( " \( self . TAG ) Chose mail. " )
self . channel = . mail
2018-05-09 17:54:10 +02:00
let picker = ContactsPicker ( allowsMultipleSelection : true , subtitleCellType : . email )
picker . contactsPickerDelegate = self
2018-05-01 22:38:54 +02:00
picker . title = NSLocalizedString ( " INVITE_FRIENDS_PICKER_TITLE " , comment : " Navbar title " )
2018-05-17 04:42:00 +02:00
let navigationController = OWSNavigationController ( rootViewController : picker )
2016-11-23 17:07:38 +01:00
self . presentingViewController . present ( navigationController , animated : true )
2016-11-18 23:11:56 +01:00
}
}
2016-11-23 17:07:38 +01:00
func sendMailTo ( emails recipientEmails : [ String ] ) {
2016-11-18 23:11:56 +01:00
let mailComposeViewController = MFMailComposeViewController ( )
mailComposeViewController . mailComposeDelegate = self
2016-11-23 17:07:38 +01:00
mailComposeViewController . setBccRecipients ( recipientEmails )
2016-11-18 23:11:56 +01:00
2018-02-23 18:46:04 +01:00
let subject = NSLocalizedString ( " EMAIL_INVITE_SUBJECT " , comment : " subject of email sent to contacts when inviting to install Signal " )
2018-03-26 15:37:43 +02:00
let bodyFormat = NSLocalizedString ( " EMAIL_INVITE_BODY " , comment : " body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to the Signal home page}} " )
2016-11-23 00:02:14 +01:00
let body = String . init ( format : bodyFormat , installUrl , homepageUrl )
2016-11-18 23:11:56 +01:00
mailComposeViewController . setSubject ( subject )
mailComposeViewController . setMessageBody ( body , isHTML : false )
self . presentingViewController . dismiss ( animated : true ) {
2018-05-25 20:27:23 +02:00
self . presentingViewController . present ( mailComposeViewController , animated : true )
2016-11-18 23:11:56 +01:00
}
}
// MARK: M a i l C o m p o s e V i e w C o n t r o l l e r D e l e g a t e
func mailComposeController ( _ controller : MFMailComposeViewController , didFinishWith result : MFMailComposeResult , error : Error ? ) {
2018-05-25 20:27:23 +02:00
self . presentingViewController . dismiss ( animated : true ) {
switch result {
case . failed :
let warning = UIAlertController ( title : nil , message : NSLocalizedString ( " SEND_INVITE_FAILURE " , comment : " Alert body after invite failed " ) , preferredStyle : . alert )
warning . addAction ( UIAlertAction ( title : CommonStrings . dismissButton , style : . default , handler : nil ) )
self . presentingViewController . present ( warning , animated : true , completion : nil )
case . sent :
Logger . debug ( " \( self . TAG ) user successfully invited their friends via mail. " )
case . saved :
Logger . debug ( " \( self . TAG ) user saved mail invite. " )
case . cancelled :
Logger . debug ( " \( self . TAG ) user cancelled mail invite. " )
}
2016-11-18 23:11:56 +01:00
}
}
}