-
Notifications
You must be signed in to change notification settings - Fork 0
[pull] main from expo:main #504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
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
) # Why Server middleware is in alpha, and should therefore display the "Alpha" tag in the sidebar. # How Added the missing `isAlpha: true` property to the document's front-matter. # Test Plan - CI # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why - Currently `List` does not support delete/move actions on partial items (check below test video). This requires `onDelete` and `onMove` modifiers to be present on `ForEach`. Great [article](https://www.hackingwithswift.com/books/ios-swiftui/deleting-items-using-ondelete) explaining it. Right now we have `onDelete` and `onMove` as `List` prop, which forces all the children to be selectable/movable. - API does not closely match SwiftUI's. Has custom props like `editModeEnabled`, `deleteEnabled` etc <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How - Added `ForEach` component with `onDelete` and `onMove` props for delete/reorder functionality. This gives ability to perform selection on few items instead of all the list items. - Added `selection` prop to List for controlled selection state - Added `environment` modifier to enable edit mode. This can be further extended to include more built-in SwiftUI environment keys. - Added `moveDisabled` and `deleteDisabled` modifiers to disable actions on individual items - Removed `editModeEnabled` prop from `List` (use environment('editMode', 'active') instead) - Added `ForEach` docs and updated `List` docs with new examples <!-- How did you build this feature or fix this bug and why? --> # Test Plan Tested example and docs locally https://github.com/user-attachments/assets/ccece344-5fcd-4bb6-bae9-a3c6fb9a8304 <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --------- Co-authored-by: Aman Mittal <[email protected]>
# Why
The old module was written using `Map/Bundle`, but now the same logic
can be implemented much better with `Record`. This change also enabled
us to easily extend the API with granular modification functions (e.g.
`contact.addPhone()`) and create a very flexible `.patch()` method.
What has changed:
* Migration from `Map/Bundle` to `Record`
* New object-oriented API, focused on easier contact modification (e.g.,
`addPhone`, `getPhones`, `deletePhone`, `updatePhone`, `patch`,
`setFirstName`)
* Significant performance optimization for fetching contacts with
pagination.
* Added the `PartialContactDetails` data type, making `getDetails`
type-safe based on the requested fields.
* Refactored code by introducing an abstraction over `ContactsContract`
and clearly separating DTO from Domain
* Migration from promises to coroutines to handle intents
* Refactored permission handling
# How
### New API
Creating a contact:
```js
const contact = await Contact.create({ givenName: "Andrew", lastName: "Jones" })
```
Granular functions for managing properties:
```js
const emailId = await contact.addEmail({
address: "[email protected]",
label: "work",
})
await contact.updateEmail({ id: emailId, address: "[email protected]", label: "home" })
await contact.deleteEmail(emailId)
```
Patch:
* distinguishes between existing and new properties
* updates only specified fields
* unlike the old module, it doesn’t wipe all data just to update the
contact
```js
await contact.patch({
givenName: "Tom",
lastName: "Smith",
company: null,
emails: [
{
id: emailId,
address: "[email protected]",
label: "work",
},
{
address: "[email protected]",
label: "home",
},
],
})
```
Fetching details:
```js
await contact.getDetails([ContactField.EMAILS, ContactField.GIVEN_NAME])
await Contact.getAllWithDetails([ContactField.EMAILS, ContactField.GIVEN_NAME])
```
Moreover, thanks to the `PartialContactDetails` data type, TypeScript
knows exactly what is inside the details object:
<img width="398" height="152" alt="Screenshot 2025-12-02 at 12 58 14"
src="https://github.com/user-attachments/assets/6b33b483-68c0-4ad1-a4e1-6732440c7a55"
/>
### Performance improvement
Due to the monolithic nature of the `Data` table in Android Contacts,
creating efficient queries with limit/offset is difficult. Because of
this, I utilized a two-step query approach, which resulted in a
significant performance improvement. In the old module, the query didn't
scale with the database size when fetching contacts with a limit; now it
scales perfectly.
| Rows in DB | Old (ms) | New (ms) | Diff (x) |
| :--- | :--- | :--- | :--- |
| 100 | 23 | 21.1 | 1.09x |
| 1000 | 179.2 | 41.4 | 4.33x |
| 5000 | 670.6 | 46.7 | 14.36x |
| 10000 | 1579.3 | 40.8 | 38.71x |
### Migration to Record (from Map/Bundle)
Thanks to new features introduced to `Record`, it became feasible to
migrate completely from `Map/Bundle`. `Map/Bundle` is extremely flexible
in terms of what can be passed to native code and returned to JS, but
this flexibility comes with a downside — the structure of objects is not
standardized, which forces constant casting.
The most important classes that enabled the migration:
* **`RecordFormatter`** — lets us decide which fields should be sent to
the JS side. No more objects filled with nulls.
* **`ValueOrUndefined`** — distinguishes between explicitly passing
`null` and leaving a field `undefined`. This matters when patching — we
need to know whether the user wants to change a property or keep it
unchanged.
* **`Either`** — previously, `Map` let us pass any type. `Either` brings
the same flexibility but keeps it within a typed environment.
### Logic Refactor
The API object structure differs from the `ContactContract`
representation.
That’s why I split the code into two domains: **Records (DTO layer)**
and **Domain (abstraction over ContactsContract)**.
The abstraction looks like this:
* Each data type stored in the Contacts `Data` table (identified by a
MIME type) is represented by an abstract `model` class.
* Model classes are extended by operation-specific types (`Existing`,
`Appendable`, `New`, `Patch`). Each implements interfaces defining how
database operations should be executed.
* Data extraction uses the `ExtractableField` interface, which defines
how to build the projection and how to read values from a cursor.
* At the core is the `ContactRepository`, which takes objects
implementing these interfaces and performs operations via
`contentResolver`.
### Migration from Intents to Coroutines
Thanks to this change, we no longer create pending promises in one
function and resolve them in another.
Instead, we use `AppContextActivityResultLauncher`, register them in
`RegisterActivityContracts`, and launch them wherever we want.
### Permissions Refactor
Similar to `MediaLibrary@Next`, I changed how permission exceptions are
handled.
The only moment where explicit permission checking matters is when
creating a `Contact` object — there’s no need to check permissions on
every method call afterward.
Because of that, `ensurePermissions` was replaced by a
`contentResolver.query` wrapper that catches security exceptions.
## Test plan
Adds tests to bare-expo
# Why Allows creating an album with a name containing spaces. # How Changes the regex # Test Plan Bare expo
# Why A `Contacts.updateContactAsync() with image` test fails `Contacts.getContactByIdAsync()` returns an image uri with "content://" scheme, but the update function accepts only "file://" scheme. So when we want to update a contact with the object returned by the get function `RemoteImageUriException` is thrown. # How Adds support for other schemas supported by ContentResolver. # Test Plan - Bare Expo - Updated failing test: `createContactWithImage` don't accept arguments added more assertions
…#39331) # Why While testing this [PR](#39322) i noticed that on each drag action all prop setters were called. Initially i thought it's reanimated/gesture handler but found that it happens even with a simple code like below. ```jsx const [translateX, setTranslateX] = useState(100); <GlassView style={[ styles.glassRect, [ { transform: [{ translateX: translateX }, { translateY: translateY }], }, ], ]} glassEffectStyle={selectedStyle} tintColor={tintColor} isInteractive={isInteractive} /> ``` Setting translateX state above will trigger `glassEffectStyle`, `tintColor` `isInteractive` prop setter functions. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How Add a shallow equality check before triggering setter function. React Native has a similar check in it's [updateProps](https://github.com/facebook/react-native/blob/b2b992c5bbfd498ecf50c7128784280b22215d85/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm#L229) function. <!-- How did you build this feature or fix this bug and why? --> # Test Plan Tested Expo views. In an ideal scenario this should not have a breaking change. But it could break if some view is relying on the behaviour of always triggering all prop setters 🤷♂️ <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )