2017-07-13 20:53:24 +02:00
//
2019-01-17 17:43:44 +01:00
// C o p y r i g h t ( c ) 2 0 1 9 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
2020-06-05 02:38:44 +02:00
import SessionServiceKit
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
}
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
2019-01-17 17:43:44 +01:00
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
2016-11-18 23:11:56 +01:00
var channel : Channel ?
2018-05-25 23:17:15 +02:00
@objc
2019-01-17 17:43:44 +01:00
required init ( presentingViewController : UIViewController ) {
2016-11-18 23:11:56 +01:00
self . presentingViewController = presentingViewController
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 {
2018-08-23 16:37:34 +02:00
Logger . info ( " Twitter not supported. " )
2016-11-18 23:11:56 +01:00
return nil
}
guard let twitterViewController = SLComposeViewController ( forServiceType : SLServiceTypeTwitter ) else {
2018-08-23 16:37:34 +02:00
Logger . error ( " unable to build twitter controller. " )
2016-11-18 23:11:56 +01:00
return nil
}
2018-07-19 01:23:22 +02:00
let tweetString = NSLocalizedString ( " SETTINGS_INVITE_TWITTER_TEXT " , comment : " content of tweet when inviting via twitter - please do not translate URL " )
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
2018-08-23 16:37:34 +02:00
Logger . debug ( " Chose tweet " )
2016-11-18 23:11:56 +01:00
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 ] ) {
2018-08-23 16:37:34 +02:00
Logger . debug ( " didSelectContacts: \( contacts ) " )
2016-11-18 23:11:56 +01:00
guard let inviteChannel = channel else {
2018-08-23 16:37:34 +02:00
Logger . error ( " 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 :
2018-08-23 16:37:34 +02:00
Logger . error ( " unexpected channel after returning from contact picker: \( inviteChannel ) " )
2016-11-18 23:11:56 +01:00
}
}
2016-11-23 17:07:38 +01:00
func contactsPicker ( _ : ContactsPicker , shouldSelectContact contact : Contact ) -> Bool {
guard let inviteChannel = channel else {
2018-08-23 16:37:34 +02:00
Logger . error ( " unexpected nil channel in contact picker. " )
2016-11-23 17:07:38 +01:00
return true
}
switch inviteChannel {
case . message :
return contact . userTextPhoneNumbers . count > 0
case . mail :
return contact . emails . count > 0
default :
2018-08-23 16:37:34 +02:00
Logger . error ( " unexpected channel after returning from contact picker: \( inviteChannel ) " )
2016-11-23 17:07:38 +01:00
}
return true
2016-11-18 23:11:56 +01:00
}
2018-05-01 22:38:54 +02:00
func contactsPicker ( _ : ContactsPicker , contactFetchDidFail error : NSError ) {
2018-08-23 16:37:34 +02:00
Logger . error ( " 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 ) {
2018-08-23 16:37:34 +02:00
Logger . debug ( " " )
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 ) {
2018-08-27 16:27:48 +02:00
owsFailDebug ( " 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 {
2018-08-23 16:37:34 +02:00
Logger . info ( " Device cannot send text " )
2016-11-18 23:11:56 +01:00
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
2018-08-23 16:37:34 +02:00
Logger . debug ( " Chose message. " )
2016-11-18 23:11:56 +01:00
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 )
2019-03-21 15:55:04 +01:00
self . presentingViewController . presentAlert ( warning )
2018-05-31 19:46:59 +02:00
} 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 ] ) {
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 ) {
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 :
2018-08-23 16:37:34 +02:00
Logger . debug ( " user successfully invited their friends via SMS. " )
2018-05-25 20:27:23 +02:00
case . cancelled :
2018-08-23 16:37:34 +02:00
Logger . debug ( " user cancelled message invite " )
2018-05-25 20:27:23 +02:00
}
2016-11-18 23:11:56 +01:00
}
}
// MARK: M a i l
func mailAction ( ) -> UIAlertAction ? {
guard MFMailComposeViewController . canSendMail ( ) else {
2018-08-23 16:37:34 +02:00
Logger . info ( " Device cannot send mail " )
2016-11-18 23:11:56 +01:00
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
2018-08-23 16:37:34 +02:00
Logger . debug ( " Chose mail. " )
2016-11-18 23:11:56 +01:00
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 :
2018-08-23 16:37:34 +02:00
Logger . debug ( " user successfully invited their friends via mail. " )
2018-05-25 20:27:23 +02:00
case . saved :
2018-08-23 16:37:34 +02:00
Logger . debug ( " user saved mail invite. " )
2018-05-25 20:27:23 +02:00
case . cancelled :
2018-08-23 16:37:34 +02:00
Logger . debug ( " user cancelled mail invite. " )
2018-05-25 20:27:23 +02:00
}
2016-11-18 23:11:56 +01:00
}
}
}