From 08f36a050848afe5b0393e8606866572b71bdbf0 Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Wed, 24 Apr 2024 10:35:58 +0200 Subject: [PATCH] move logic over to MailManager --- ios/StatusIm.xcodeproj/project.pbxproj | 6 + .../im/status/ethereum/module/MailManager.kt | 133 +++++++++++ .../status/ethereum/module/NetworkManager.kt | 107 --------- .../status/ethereum/module/StatusPackage.kt | 1 + .../ios/RCTStatus/MailManager.h | 9 + .../ios/RCTStatus/MailManager.m | 207 ++++++++++++++++++ .../ios/RCTStatus/NetworkManager.h | 2 +- .../ios/RCTStatus/NetworkManager.m | 198 ----------------- .../RCTStatus.xcodeproj/project.pbxproj | 6 + src/native_module/core.cljs | 7 +- 10 files changed, 369 insertions(+), 307 deletions(-) create mode 100644 modules/react-native-status/android/src/main/java/im/status/ethereum/module/MailManager.kt create mode 100644 modules/react-native-status/ios/RCTStatus/MailManager.h create mode 100644 modules/react-native-status/ios/RCTStatus/MailManager.m diff --git a/ios/StatusIm.xcodeproj/project.pbxproj b/ios/StatusIm.xcodeproj/project.pbxproj index 6d6478fe8167..870c2301f194 100644 --- a/ios/StatusIm.xcodeproj/project.pbxproj +++ b/ios/StatusIm.xcodeproj/project.pbxproj @@ -596,12 +596,14 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Status-StatusIm-StatusImTests/Pods-Status-StatusIm-StatusImTests-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", ); @@ -668,12 +670,14 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Status-StatusIm/Pods-Status-StatusIm-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", ); @@ -690,12 +694,14 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Status-StatusImPR/Pods-Status-StatusImPR-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNPermissions/RNPermissionsPrivacyInfo.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNPermissionsPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", ); diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/MailManager.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/MailManager.kt new file mode 100644 index 000000000000..c5173bb9dc52 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/MailManager.kt @@ -0,0 +1,133 @@ +package im.status.ethereum.module + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.Callback +import android.util.Log +import android.content.Intent +import android.net.Uri +import android.content.pm.PackageManager +import java.io.File +import androidx.core.content.FileProvider +import android.text.Html + +class MailManager(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + + private val utils = Utils(reactContext) + + override fun getName() = "MailManager" + + @ReactMethod + fun mail(options: ReadableMap, callback: Callback) { + Log.d(TAG, "attempting to send email") + val i = Intent(Intent.ACTION_SEND_MULTIPLE) + val selectorIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) + i.selector = selectorIntent + + if (options.hasKey("subject") && !options.isNull("subject")) { + i.putExtra(Intent.EXTRA_SUBJECT, options.getString("subject")) + } + + if (options.hasKey("body") && !options.isNull("body")) { + val body = options.getString("body") + if (options.hasKey("isHTML") && options.getBoolean("isHTML")) { + i.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(body)) + } else { + i.putExtra(Intent.EXTRA_TEXT, body) + } + } + + if (options.hasKey("recipients") && !options.isNull("recipients")) { + val recipients = options.getArray("recipients") + i.putExtra(Intent.EXTRA_EMAIL, this.utils.readableArrayToStringArray(recipients!!)) + } + + if (options.hasKey("ccRecipients") && !options.isNull("ccRecipients")) { + val ccRecipients = options.getArray("ccRecipients") + i.putExtra(Intent.EXTRA_CC, this.utils.readableArrayToStringArray(ccRecipients!!)) + } + + if (options.hasKey("bccRecipients") && !options.isNull("bccRecipients")) { + val bccRecipients = options.getArray("bccRecipients") + i.putExtra(Intent.EXTRA_BCC, this.utils.readableArrayToStringArray(bccRecipients!!)) + } + + if (options.hasKey("attachments") && !options.isNull("attachments")) { + val r = options.getArray("attachments") + val length = r?.size() ?: 0 + + val provider = reactContext.applicationContext.packageName + ".rnmail.provider" + val resolvedIntentActivities = reactContext.packageManager.queryIntentActivities(i, + PackageManager.MATCH_DEFAULT_ONLY) + + val uris = ArrayList() + for (keyIndex in 0 until length) { + val clip = r?.getMap(keyIndex) + val uri: Uri + if (clip?.hasKey("path") == true && !clip.isNull("path")) { + val path = clip.getString("path") + val file = File(path) + uri = FileProvider.getUriForFile(reactContext, provider, file) + } else if (clip?.hasKey("uri") == true && !clip.isNull("uri")) { + val uriPath = clip.getString("uri") + uri = Uri.parse(uriPath) + } else { + callback.invoke("not_found") + return + } + uris.add(uri) + + for (resolvedIntentInfo in resolvedIntentActivities) { + val packageName = resolvedIntentInfo.activityInfo.packageName + reactContext.grantUriPermission(packageName, uri, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + } + + i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris) + } + + val manager = reactContext.packageManager + val list = manager.queryIntentActivities(i, 0) + + if (list == null || list.isEmpty()) { + Log.d(TAG, "not_available") + callback.invoke("not_available") + return + } + + if (list.size == 1) { + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + try { + reactContext.startActivity(i) + } catch (ex: Exception) { + Log.e(TAG, ex.message!!) + callback.invoke("error") + } + } else { + var chooserTitle = "Send Mail" + + if (options.hasKey("customChooserTitle") && !options.isNull("customChooserTitle")) { + chooserTitle = options.getString("customChooserTitle") ?: "" + } + + val chooser = Intent.createChooser(i, chooserTitle) + chooser.flags = Intent.FLAG_ACTIVITY_NEW_TASK + + try { + reactContext.startActivity(chooser) + } catch (ex: Exception) { + Log.e(TAG, ex.message!!) + callback.invoke("error") + } + } + } + + companion object { + private const val TAG = "MailManager" + } + +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt index 29b652c00caa..1ed883b06fb4 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/NetworkManager.kt @@ -84,113 +84,6 @@ class NetworkManager(private val reactContext: ReactApplicationContext) : ReactC utils.executeRunnableStatusGoMethod({ Statusgo.recover(rpcParams) }, callback) } - @ReactMethod - fun mail(options: ReadableMap, callback: Callback) { - Log.d(TAG, "Just starting mail") - val i = Intent(Intent.ACTION_SEND_MULTIPLE) - val selectorIntent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")) - i.selector = selectorIntent - - if (options.hasKey("subject") && !options.isNull("subject")) { - i.putExtra(Intent.EXTRA_SUBJECT, options.getString("subject")) - } - - if (options.hasKey("body") && !options.isNull("body")) { - val body = options.getString("body") - if (options.hasKey("isHTML") && options.getBoolean("isHTML")) { - i.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(body)) - } else { - i.putExtra(Intent.EXTRA_TEXT, body) - } - } - - if (options.hasKey("recipients") && !options.isNull("recipients")) { - val recipients = options.getArray("recipients") - i.putExtra(Intent.EXTRA_EMAIL, this.utils.readableArrayToStringArray(recipients!!)) - } - - if (options.hasKey("ccRecipients") && !options.isNull("ccRecipients")) { - val ccRecipients = options.getArray("ccRecipients") - i.putExtra(Intent.EXTRA_CC, this.utils.readableArrayToStringArray(ccRecipients!!)) - } - - if (options.hasKey("bccRecipients") && !options.isNull("bccRecipients")) { - val bccRecipients = options.getArray("bccRecipients") - i.putExtra(Intent.EXTRA_BCC, this.utils.readableArrayToStringArray(bccRecipients!!)) - } - - if (options.hasKey("attachments") && !options.isNull("attachments")) { - val r = options.getArray("attachments") - val length = r?.size() ?: 0 - - val provider = reactContext.applicationContext.packageName + ".rnmail.provider" - val resolvedIntentActivities = reactContext.packageManager.queryIntentActivities(i, - PackageManager.MATCH_DEFAULT_ONLY) - - val uris = ArrayList() - for (keyIndex in 0 until length) { - val clip = r?.getMap(keyIndex) - val uri: Uri - if (clip?.hasKey("path") == true && !clip.isNull("path")) { - val path = clip.getString("path") - val file = File(path) - uri = FileProvider.getUriForFile(reactContext, provider, file) - } else if (clip?.hasKey("uri") == true && !clip.isNull("uri")) { - val uriPath = clip.getString("uri") - uri = Uri.parse(uriPath) - } else { - callback.invoke("not_found") - return - } - uris.add(uri) - - for (resolvedIntentInfo in resolvedIntentActivities) { - val packageName = resolvedIntentInfo.activityInfo.packageName - reactContext.grantUriPermission(packageName, uri, - Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) - } - } - - i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - i.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris) - } - - val manager = reactContext.packageManager - val list = manager.queryIntentActivities(i, 0) - - if (list == null || list.isEmpty()) { - Log.d(TAG, "not_available") - callback.invoke("not_available") - return - } - - if (list.size == 1) { - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - try { - reactContext.startActivity(i) - } catch (ex: Exception) { - Log.e(TAG, ex.message!!) - callback.invoke("error") - } - } else { - var chooserTitle = "Send Mail" - - if (options.hasKey("customChooserTitle") && !options.isNull("customChooserTitle")) { - chooserTitle = options.getString("customChooserTitle") ?: "" - } - - val chooser = Intent.createChooser(i, chooserTitle) - chooser.flags = Intent.FLAG_ACTIVITY_NEW_TASK - - try { - reactContext.startActivity(chooser) - } catch (ex: Exception) { - Log.e(TAG, ex.message!!) - callback.invoke("error") - } - } - } - companion object { private const val TAG = "NetworkManager" } diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt index 738f9f0768de..4acb25b9107f 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt @@ -24,6 +24,7 @@ class StatusPackage(private val rootedDevice: Boolean) : ReactPackage { add(LogManager(reactContext)) add(Utils(reactContext)) add(NetworkManager(reactContext)) + add(MailManager(reactContext)) add(RNSelectableTextInputModule(reactContext)) } diff --git a/modules/react-native-status/ios/RCTStatus/MailManager.h b/modules/react-native-status/ios/RCTStatus/MailManager.h new file mode 100644 index 000000000000..98a4fa1303e6 --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/MailManager.h @@ -0,0 +1,9 @@ +#import +#import +#import +#import +#import "RCTLog.h" + +@interface MailManager : NSObject + +@end diff --git a/modules/react-native-status/ios/RCTStatus/MailManager.m b/modules/react-native-status/ios/RCTStatus/MailManager.m new file mode 100644 index 000000000000..bc0a36b7a9d1 --- /dev/null +++ b/modules/react-native-status/ios/RCTStatus/MailManager.m @@ -0,0 +1,207 @@ +#import +#import "MailManager.h" +#import +#import +#import "React/RCTBridge.h" +#import "React/RCTEventDispatcher.h" + +@implementation MailManager +{ + NSMutableDictionary *_callbacks; +} + +- (instancetype)init +{ + if ((self = [super init])) { + _callbacks = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + ++ (BOOL)requiresMainQueueSetup +{ + return YES; +} + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD(mail:(NSDictionary *)options + callback: (RCTResponseSenderBlock)callback) +{ + if ([MFMailComposeViewController canSendMail]) + { + MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init]; + mail.mailComposeDelegate = self; + _callbacks[RCTKeyForInstance(mail)] = callback; + + if (options[@"subject"]){ + NSString *subject = [RCTConvert NSString:options[@"subject"]]; + [mail setSubject:subject]; + } + + BOOL isHTML = NO; + + if (options[@"isHTML"]){ + isHTML = [options[@"isHTML"] boolValue]; + } + + if (options[@"body"]){ + NSString *body = [RCTConvert NSString:options[@"body"]]; + [mail setMessageBody:body isHTML:isHTML]; + } + + if (options[@"recipients"]){ + NSArray *recipients = [RCTConvert NSArray:options[@"recipients"]]; + [mail setToRecipients:recipients]; + } + + if (options[@"ccRecipients"]){ + NSArray *ccRecipients = [RCTConvert NSArray:options[@"ccRecipients"]]; + [mail setCcRecipients:ccRecipients]; + } + + if (options[@"bccRecipients"]){ + NSArray *bccRecipients = [RCTConvert NSArray:options[@"bccRecipients"]]; + [mail setBccRecipients:bccRecipients]; + } + if (options[@"attachments"]) { + NSArray *attachments = [RCTConvert NSArray:options[@"attachments"]]; + for (NSDictionary *attachment in attachments) { + if ((attachment[@"path"] || attachment[@"uri"]) && (attachment[@"type"] || attachment[@"mimeType"])) { + NSString *attachmentPath = [RCTConvert NSString:attachment[@"path"]]; + NSString *attachmentUri = [RCTConvert NSString:attachment[@"uri"]]; + NSString *attachmentType = [RCTConvert NSString:attachment[@"type"]]; + NSString *attachmentName = [RCTConvert NSString:attachment[@"name"]]; + NSString *attachmentMimeType = [RCTConvert NSString:attachment[@"mimeType"]]; + + // Set default filename if not specificed + if (!attachmentName) { + attachmentName = [[attachmentPath lastPathComponent] stringByDeletingPathExtension]; + } + + NSData *fileData; + if (attachmentPath) { + NSFileManager *fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:attachmentPath]){ + callback(@[[NSString stringWithFormat: @"attachment file with path '%@' does not exist", attachmentPath]]); + return; + } + // Get the resource path and read the file using NSData + fileData = [NSData dataWithContentsOfFile:attachmentPath]; + } else if (attachmentUri) { + // Get the URI and read it using NSData + NSURL *attachmentURL = [[NSURLComponents componentsWithString:attachmentUri] URL]; + NSError *error = nil; + fileData = [NSData dataWithContentsOfURL:attachmentURL options:0 error:&error]; + if (!fileData) { + callback(@[[NSString stringWithFormat: @"attachment file with uri '%@' does not exist", attachmentUri]]); + return; + } + } + + // Determine the MIME type + NSString *mimeType; + if (attachmentType) { + /* + * Add additional mime types and PR if necessary. Find the list + * of supported formats at http://www.iana.org/assignments/media-types/media-types.xhtml + */ + NSDictionary *supportedMimeTypes = @{ + @"jpeg" : @"image/jpeg", + @"jpg" : @"image/jpeg", + @"png" : @"image/png", + @"doc" : @"application/msword", + @"docx" : @"application/vnd.openxmlformats-officedocument.wordprocessingml.document", + @"ppt" : @"application/vnd.ms-powerpoint", + @"pptx" : @"application/vnd.openxmlformats-officedocument.presentationml.presentation", + @"html" : @"text/html", + @"csv" : @"text/csv", + @"pdf" : @"application/pdf", + @"vcard" : @"text/vcard", + @"json" : @"application/json", + @"zip" : @"application/zip", + @"text" : @"text/*", + @"mp3" : @"audio/mpeg", + @"wav" : @"audio/wav", + @"aiff" : @"audio/aiff", + @"flac" : @"audio/flac", + @"ogg" : @"audio/ogg", + @"xls" : @"application/vnd.ms-excel", + @"ics" : @"text/calendar", + @"xlsx" : @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + }; + if([supportedMimeTypes objectForKey:attachmentType]) { + mimeType = [supportedMimeTypes objectForKey:attachmentType]; + } else { + callback(@[[NSString stringWithFormat: @"Mime type '%@' for attachment is not handled", attachmentType]]); + return; + } + } else if (attachmentMimeType) { + mimeType = attachmentMimeType; + } + + // Add attachment + [mail addAttachmentData:fileData mimeType:mimeType fileName:attachmentName]; + } + } + } + + UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + + while (root.presentedViewController) { + root = root.presentedViewController; + } + [root presentViewController:mail animated:YES completion:nil]; + } else { + callback(@[@"not_available"]); + } +} + +#pragma mark MFMailComposeViewControllerDelegate Methods + +- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error +{ + NSString *key = RCTKeyForInstance(controller); + RCTResponseSenderBlock callback = _callbacks[key]; + if (callback) { + switch (result) { + case MFMailComposeResultSent: + callback(@[[NSNull null] , @"sent"]); + break; + case MFMailComposeResultSaved: + callback(@[[NSNull null] , @"saved"]); + break; + case MFMailComposeResultCancelled: + callback(@[[NSNull null] , @"cancelled"]); + break; + case MFMailComposeResultFailed: + callback(@[@"failed"]); + break; + default: + callback(@[@"error"]); + break; + } + [_callbacks removeObjectForKey:key]; + } else { + RCTLogWarn(@"No callback registered for mail: %@", controller.title); + } + UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + while (ctrl.presentedViewController && ctrl != controller) { + ctrl = ctrl.presentedViewController; + } + [ctrl dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark Private + +static NSString *RCTKeyForInstance(id instance) +{ + return [NSString stringWithFormat:@"%p", instance]; +} + +@end diff --git a/modules/react-native-status/ios/RCTStatus/NetworkManager.h b/modules/react-native-status/ios/RCTStatus/NetworkManager.h index ccba561bbb4a..cc8ce42f98ef 100644 --- a/modules/react-native-status/ios/RCTStatus/NetworkManager.h +++ b/modules/react-native-status/ios/RCTStatus/NetworkManager.h @@ -4,6 +4,6 @@ #import "Statusgo.h" #import "RCTLog.h" -@interface NetworkManager : NSObject +@interface NetworkManager : NSObject @end diff --git a/modules/react-native-status/ios/RCTStatus/NetworkManager.m b/modules/react-native-status/ios/RCTStatus/NetworkManager.m index 64aa48d70051..c82448be72cf 100644 --- a/modules/react-native-status/ios/RCTStatus/NetworkManager.m +++ b/modules/react-native-status/ios/RCTStatus/NetworkManager.m @@ -1,6 +1,3 @@ -#import -#import -#import #import "NetworkManager.h" #import "React/RCTBridge.h" #import "React/RCTEventDispatcher.h" @@ -8,27 +5,6 @@ #import "Utils.h" @implementation NetworkManager -{ - NSMutableDictionary *_callbacks; -} - -- (instancetype)init -{ - if ((self = [super init])) { - _callbacks = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (dispatch_queue_t)methodQueue -{ - return dispatch_get_main_queue(); -} - -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} RCT_EXPORT_MODULE(); @@ -130,178 +106,4 @@ + (BOOL)requiresMainQueueSetup callback(@[result]); } -RCT_EXPORT_METHOD(mail:(NSDictionary *)options - callback: (RCTResponseSenderBlock)callback) -{ - if ([MFMailComposeViewController canSendMail]) - { - MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init]; - mail.mailComposeDelegate = self; - _callbacks[RCTKeyForInstance(mail)] = callback; - - if (options[@"subject"]){ - NSString *subject = [RCTConvert NSString:options[@"subject"]]; - [mail setSubject:subject]; - } - - BOOL isHTML = NO; - - if (options[@"isHTML"]){ - isHTML = [options[@"isHTML"] boolValue]; - } - - if (options[@"body"]){ - NSString *body = [RCTConvert NSString:options[@"body"]]; - [mail setMessageBody:body isHTML:isHTML]; - } - - if (options[@"recipients"]){ - NSArray *recipients = [RCTConvert NSArray:options[@"recipients"]]; - [mail setToRecipients:recipients]; - } - - if (options[@"ccRecipients"]){ - NSArray *ccRecipients = [RCTConvert NSArray:options[@"ccRecipients"]]; - [mail setCcRecipients:ccRecipients]; - } - - if (options[@"bccRecipients"]){ - NSArray *bccRecipients = [RCTConvert NSArray:options[@"bccRecipients"]]; - [mail setBccRecipients:bccRecipients]; - } - if (options[@"attachments"]) { - NSArray *attachments = [RCTConvert NSArray:options[@"attachments"]]; - for (NSDictionary *attachment in attachments) { - if ((attachment[@"path"] || attachment[@"uri"]) && (attachment[@"type"] || attachment[@"mimeType"])) { - NSString *attachmentPath = [RCTConvert NSString:attachment[@"path"]]; - NSString *attachmentUri = [RCTConvert NSString:attachment[@"uri"]]; - NSString *attachmentType = [RCTConvert NSString:attachment[@"type"]]; - NSString *attachmentName = [RCTConvert NSString:attachment[@"name"]]; - NSString *attachmentMimeType = [RCTConvert NSString:attachment[@"mimeType"]]; - - // Set default filename if not specificed - if (!attachmentName) { - attachmentName = [[attachmentPath lastPathComponent] stringByDeletingPathExtension]; - } - - NSData *fileData; - if (attachmentPath) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - if (![fileManager fileExistsAtPath:attachmentPath]){ - callback(@[[NSString stringWithFormat: @"attachment file with path '%@' does not exist", attachmentPath]]); - return; - } - // Get the resource path and read the file using NSData - fileData = [NSData dataWithContentsOfFile:attachmentPath]; - } else if (attachmentUri) { - // Get the URI and read it using NSData - NSURL *attachmentURL = [[NSURLComponents componentsWithString:attachmentUri] URL]; - NSError *error = nil; - fileData = [NSData dataWithContentsOfURL:attachmentURL options:0 error:&error]; - if (!fileData) { - callback(@[[NSString stringWithFormat: @"attachment file with uri '%@' does not exist", attachmentUri]]); - return; - } - } - - // Determine the MIME type - NSString *mimeType; - if (attachmentType) { - /* - * Add additional mime types and PR if necessary. Find the list - * of supported formats at http://www.iana.org/assignments/media-types/media-types.xhtml - */ - NSDictionary *supportedMimeTypes = @{ - @"jpeg" : @"image/jpeg", - @"jpg" : @"image/jpeg", - @"png" : @"image/png", - @"doc" : @"application/msword", - @"docx" : @"application/vnd.openxmlformats-officedocument.wordprocessingml.document", - @"ppt" : @"application/vnd.ms-powerpoint", - @"pptx" : @"application/vnd.openxmlformats-officedocument.presentationml.presentation", - @"html" : @"text/html", - @"csv" : @"text/csv", - @"pdf" : @"application/pdf", - @"vcard" : @"text/vcard", - @"json" : @"application/json", - @"zip" : @"application/zip", - @"text" : @"text/*", - @"mp3" : @"audio/mpeg", - @"wav" : @"audio/wav", - @"aiff" : @"audio/aiff", - @"flac" : @"audio/flac", - @"ogg" : @"audio/ogg", - @"xls" : @"application/vnd.ms-excel", - @"ics" : @"text/calendar", - @"xlsx" : @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - }; - if([supportedMimeTypes objectForKey:attachmentType]) { - mimeType = [supportedMimeTypes objectForKey:attachmentType]; - } else { - callback(@[[NSString stringWithFormat: @"Mime type '%@' for attachment is not handled", attachmentType]]); - return; - } - } else if (attachmentMimeType) { - mimeType = attachmentMimeType; - } - - // Add attachment - [mail addAttachmentData:fileData mimeType:mimeType fileName:attachmentName]; - } - } - } - - UIViewController *root = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - - while (root.presentedViewController) { - root = root.presentedViewController; - } - [root presentViewController:mail animated:YES completion:nil]; - } else { - callback(@[@"not_available"]); - } -} - -#pragma mark MFMailComposeViewControllerDelegate Methods - -- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error -{ - NSString *key = RCTKeyForInstance(controller); - RCTResponseSenderBlock callback = _callbacks[key]; - if (callback) { - switch (result) { - case MFMailComposeResultSent: - callback(@[[NSNull null] , @"sent"]); - break; - case MFMailComposeResultSaved: - callback(@[[NSNull null] , @"saved"]); - break; - case MFMailComposeResultCancelled: - callback(@[[NSNull null] , @"cancelled"]); - break; - case MFMailComposeResultFailed: - callback(@[@"failed"]); - break; - default: - callback(@[@"error"]); - break; - } - [_callbacks removeObjectForKey:key]; - } else { - RCTLogWarn(@"No callback registered for mail: %@", controller.title); - } - UIViewController *ctrl = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - while (ctrl.presentedViewController && ctrl != controller) { - ctrl = ctrl.presentedViewController; - } - [ctrl dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark Private - -static NSString *RCTKeyForInstance(id instance) -{ - return [NSString stringWithFormat:@"%p", instance]; -} - @end diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj b/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj index 352f933e5614..cc3cb7e6fe56 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ CE4E31B11D86951A0033ED64 /* Statusgo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE4E31B01D86951A0033ED64 /* Statusgo.xcframework */; }; E92244EB2B485F2400915F4C /* UIHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E92244E92B485F2400915F4C /* UIHelper.m */; }; E967A3AC2B47BD5A00FB19B2 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = E967A3AB2B47BD5A00FB19B2 /* Utils.m */; }; + E9AEB5FE2BD8EEB100FB2926 /* MailManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9AEB5FD2BD8EEB100FB2926 /* MailManager.m */; }; E9BEF3602B470BF1001F6755 /* NetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9BEF35E2B470BF1001F6755 /* NetworkManager.m */; }; E9C33AA62B4828A60074B1C5 /* DatabaseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9C33AA52B4828A60074B1C5 /* DatabaseManager.m */; }; E9DB08932B4858B400F51053 /* LogManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9DB08912B4858B400F51053 /* LogManager.m */; }; @@ -42,6 +43,8 @@ E92244EA2B485F2400915F4C /* UIHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIHelper.h; sourceTree = ""; }; E967A3AA2B47BD5A00FB19B2 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; E967A3AB2B47BD5A00FB19B2 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = ""; }; + E9AEB5FC2BD8EEB100FB2926 /* MailManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MailManager.h; sourceTree = ""; }; + E9AEB5FD2BD8EEB100FB2926 /* MailManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MailManager.m; sourceTree = ""; }; E9BEF35E2B470BF1001F6755 /* NetworkManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkManager.m; sourceTree = ""; }; E9BEF35F2B470BF1001F6755 /* NetworkManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkManager.h; sourceTree = ""; }; E9C33AA42B4828A60074B1C5 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = ""; }; @@ -86,6 +89,8 @@ 206C9F3C1D474E910063E3E6 /* Status */ = { isa = PBXGroup; children = ( + E9AEB5FC2BD8EEB100FB2926 /* MailManager.h */, + E9AEB5FD2BD8EEB100FB2926 /* MailManager.m */, E92244EA2B485F2400915F4C /* UIHelper.h */, E92244E92B485F2400915F4C /* UIHelper.m */, E9DB08922B4858B400F51053 /* LogManager.h */, @@ -163,6 +168,7 @@ buildActionMask = 2147483647; files = ( E9DB08932B4858B400F51053 /* LogManager.m in Sources */, + E9AEB5FE2BD8EEB100FB2926 /* MailManager.m in Sources */, E9F5C3322B483B6C001A7F40 /* EncryptionUtils.m in Sources */, E967A3AC2B47BD5A00FB19B2 /* Utils.m in Sources */, E92244EB2B485F2400915F4C /* UIHelper.m in Sources */, diff --git a/src/native_module/core.cljs b/src/native_module/core.cljs index d926fb3a5293..611d9d2ddd6e 100644 --- a/src/native_module/core.cljs +++ b/src/native_module/core.cljs @@ -46,9 +46,14 @@ (when (exists? (.-NativeModules react-native)) (.-NetworkManager ^js (.-NativeModules react-native)))) +(defn mail-manager + [] + (when (exists? (.-NativeModules react-native)) + (.-MailManager ^js (.-NativeModules react-native)))) + (defn mail [opts callback] - (.mail ^js (network) (clj->js opts) callback)) + (.mail ^js (mail-manager) (clj->js opts) callback)) (defn init [handler]