Skip to content

Commit

Permalink
[contacts] Fix editing contacts on both platforms (#27703)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanjhughes committed Mar 15, 2024
1 parent dd80015 commit 773ab9e
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 59 deletions.
Expand Up @@ -31,21 +31,21 @@ export default function ContactDetailScreen(props: any) {
headerRight: () => (
<HeaderContainerRight>
<HeaderIconButton
name="md-share"
name="share"
onPress={async () => {
Contacts.shareContactAsync(props.route.params.id, 'Call me :]');
}}
/>
<HeaderIconButton
name="md-open"
name="open"
onPress={async () => {
await Contacts.presentFormAsync(props.route.params.id);
console.log('the native contact form has been closed');
}}
/>
{isIos && (
<HeaderIconButton
name="md-copy"
name="copy"
onPress={async () => {
await ContactUtils.cloneAsync(props.route.params.id);
props.navigation.goBack();
Expand Down Expand Up @@ -338,7 +338,7 @@ function LinkedButton({
backgroundColor,
},
]}>
<Ionicons name={`ios-${icon}` as any} size={20} color={color} />
<Ionicons name={icon as any} size={20} color={color} />
</View>
<Text style={[styles.linkButtonText, { color: backgroundColor }]}>{text}</Text>
</TouchableOpacity>
Expand Down
Expand Up @@ -30,7 +30,7 @@ export default function ContactsScreen({ navigation }: Props) {
<HeaderContainerRight>
<HeaderIconButton
disabled={Platform.select({ web: true, default: false })}
name="md-add"
name="add"
onPress={() => {
const randomContact = { note: 'Likes expo...' } as Contacts.Contact;
ContactUtils.presentNewContactFormAsync({ contact: randomContact });
Expand Down
2 changes: 2 additions & 0 deletions packages/expo-contacts/CHANGELOG.md
Expand Up @@ -8,6 +8,8 @@

### 🐛 Bug fixes

- Fixed an issue where contacts could not be edited on either platform. ([#27703](https://github.com/expo/expo/pull/27703) by [@alanjhughes](https://github.com/alanjhughes))

### 💡 Others

- drop unused web `name` property. ([#27437](https://github.com/expo/expo/pull/27437) by [@EvanBacon](https://github.com/EvanBacon))
Expand Down
Expand Up @@ -18,10 +18,6 @@ import expo.modules.contacts.models.PhoneNumberModel
import expo.modules.contacts.models.PostalAddressModel
import expo.modules.contacts.models.RelationshipModel
import expo.modules.contacts.models.UrlAddressModel
import expo.modules.core.ModuleRegistry
import expo.modules.core.interfaces.ActivityEventListener
import expo.modules.core.interfaces.ActivityProvider
import expo.modules.core.interfaces.services.UIManager
import expo.modules.interfaces.permissions.Permissions
import expo.modules.kotlin.Promise
import expo.modules.kotlin.exception.CodedException
Expand Down Expand Up @@ -134,8 +130,6 @@ class QueryArguments(
)

class ContactsModule : Module() {
private val mActivityEventListener: ActivityEventListener = ContactsActivityEventListener()
private var mModuleRegistry: ModuleRegistry? = null
private var mPendingPromise: Promise? = null

private val permissionsManager: Permissions
Expand All @@ -147,18 +141,6 @@ class ContactsModule : Module() {
override fun definition() = ModuleDefinition {
Name("ExpoContacts")

OnCreate {
appContext
.legacyModule<UIManager>()
?.registerActivityEventListener(mActivityEventListener)
}

OnDestroy {
appContext
.legacyModule<UIManager>()
?.unregisterActivityEventListener(mActivityEventListener)
}

AsyncFunction("requestPermissionsAsync") { promise: Promise ->
if (permissionsManager.isPermissionPresentInManifest(Manifest.permission.WRITE_CONTACTS)) {
Permissions.askForPermissionsWithPermissionsManager(permissionsManager, promise, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)
Expand Down Expand Up @@ -272,7 +254,7 @@ class ContactsModule : Module() {
uri.toString()
}

AsyncFunction("presentFormAsync") { contactId: String?, contactData: Map<String, Any>, options: Map<String, Any?>?, promise: Promise ->
AsyncFunction("presentFormAsync") { contactId: String?, contactData: Map<String, Any>?, options: Map<String, Any?>?, promise: Promise ->
ensureReadPermission()

if (contactId != null) {
Expand All @@ -281,8 +263,20 @@ class ContactsModule : Module() {
return@AsyncFunction
}
// Create contact from supplied data.
val contact = mutateContact(null, contactData)
presentForm(contact)
if (contactData != null) {
val contact = mutateContact(null, contactData)
mPendingPromise = promise
presentForm(contact)
}
promise.resolve()
}

OnActivityResult { _, payload ->
val (requestCode, _, _) = payload
val pendingPromise = mPendingPromise ?: return@OnActivityResult
if (requestCode == RC_EDIT_CONTACT) {
pendingPromise.resolve(0)
}
}
}

Expand All @@ -291,8 +285,7 @@ class ContactsModule : Module() {
intent.putExtra(ContactsContract.Intents.Insert.NAME, contact.getFinalDisplayName())
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contact.contentValues)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val activityProvider = mModuleRegistry!!.getModule(ActivityProvider::class.java)
activityProvider.currentActivity.startActivity(intent)
activity.startActivity(intent)
}

private fun presentEditForm(contact: Contact, promise: Promise) {
Expand All @@ -302,9 +295,8 @@ class ContactsModule : Module() {
)
val intent = Intent(Intent.ACTION_EDIT)
intent.setDataAndType(selectedContactUri, ContactsContract.Contacts.CONTENT_ITEM_TYPE)
val activityProvider = mModuleRegistry!!.getModule(ActivityProvider::class.java)
mPendingPromise = promise
activityProvider.currentActivity.startActivityForResult(intent, RC_EDIT_CONTACT)
activity.startActivityForResult(intent, RC_EDIT_CONTACT)
}

private val resolver: ContentResolver
Expand Down Expand Up @@ -663,17 +655,6 @@ class ContactsModule : Module() {
ensureReadPermission()
ensureWritePermission()
}

private inner class ContactsActivityEventListener : ActivityEventListener {
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) {
val pendingPromise = mPendingPromise ?: return
if (requestCode == RC_EDIT_CONTACT) {
pendingPromise.resolve(0)
}
}

override fun onNewIntent(intent: Intent) = Unit
}
}

fun <T> Map<String, Any>.safeGet(key: String): T? {
Expand Down
2 changes: 1 addition & 1 deletion packages/expo-contacts/build/Contacts.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-contacts/build/Contacts.js.map

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions packages/expo-contacts/ios/ContactsModule.swift
Expand Up @@ -22,7 +22,7 @@ public class ContactsModule: Module {

AsyncFunction("writeContactToFileAsync") { (options: ContactsQuery) -> String? in
let keys = contactKeysToFetch(from: options.fields)
let payload = fetchContactsData(options: options, keys: keys)
let payload = fetchContactsData(options: options, keys: keys, isWriting: true)

if let error = payload["error"] {
throw FailedToFetchContactsException()
Expand Down Expand Up @@ -70,22 +70,22 @@ public class ContactsModule: Module {
}
}.runOnQueue(.main)

AsyncFunction("presentFormAsync") { (identifier: String?, data: Contact, options: FormOptions, promise: Promise) in
AsyncFunction("presentFormAsync") { (identifier: String?, data: Contact?, options: FormOptions, promise: Promise) in
var controller: ContactsViewController?
var contact: CNMutableContact

if let identifier {
if let foundContact = try getContact(withId: identifier) as? CNMutableContact {
contact = foundContact
controller = ContactsViewController.init(forNewContact: contact)
if let foundContact = try? getContact(withId: identifier) {
controller = ContactsViewController.init(forNewContact: foundContact)
}
} else {
contact = CNMutableContact()
try mutateContact(&contact, with: data)
if options.isNew == true {
controller = ContactsViewController.init(forNewContact: contact)
} else {
controller = ContactsViewController.init(forUnknownContact: contact)
var contact = CNMutableContact()
if let data {
try mutateContact(&contact, with: data)
if options.isNew == true {
controller = ContactsViewController.init(forNewContact: contact)
} else {
controller = ContactsViewController.init(forUnknownContact: contact)
}
}
}

Expand Down Expand Up @@ -505,7 +505,7 @@ public class ContactsModule: Module {
}
}

private func fetchContactsData(options: ContactsQuery, keys: [String]) -> [String: Any] {
private func fetchContactsData(options: ContactsQuery, keys: [String], isWriting: Bool = false) -> [String: Any] {
var predicate: NSPredicate?

if let id = options.id {
Expand All @@ -518,7 +518,7 @@ public class ContactsModule: Module {
predicate = CNContact.predicateForContacts(withIdentifiers: [containerId])
}

var descriptors = getDescriptors(for: keys)
var descriptors = getDescriptors(for: keys, isWriting: isWriting)
return queryContacts(with: predicate, keys: descriptors, options: options)
}

Expand Down
5 changes: 4 additions & 1 deletion packages/expo-contacts/ios/Serialization.swift
Expand Up @@ -182,9 +182,12 @@ func contactKeysToFetch(from fields: [String]?) -> [String] {
return results
}

func getDescriptors(for fields: [String]?) -> [CNKeyDescriptor] {
func getDescriptors(for fields: [String]?, isWriting: Bool = false) -> [CNKeyDescriptor] {
let keys = contactKeysToFetch(from: fields)
var descriptors = keys as [CNKeyDescriptor]
if isWriting {
descriptors.append(CNContactVCardSerialization.descriptorForRequiredKeys())
}

if keys.contains(ContactsKey.name) {
descriptors.append(CNContactFormatter.descriptorForRequiredKeys(for: .fullName))
Expand Down
2 changes: 1 addition & 1 deletion packages/expo-contacts/src/Contacts.ts
Expand Up @@ -649,7 +649,7 @@ export async function getContactByIdAsync(
pageSize: 1,
pageOffset: 0,
fields,
id: Array.isArray(id) ? id : [id],
id,
});
if (results && results.data && results.data.length > 0) {
return results.data[0];
Expand Down

0 comments on commit 773ab9e

Please sign in to comment.