aix 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -159
- package/android/src/main/java/com/aix/HybridAix.kt +21 -11
- package/ios/HybridAix.swift +19 -9
- package/lib/typescript/src/aix.d.ts +2 -0
- package/lib/typescript/src/aix.d.ts.map +1 -1
- package/lib/typescript/src/views/aix.nitro.d.ts +7 -0
- package/lib/typescript/src/views/aix.nitro.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridAixSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridAixSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/views/JHybridAixStateUpdater.cpp +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/aix/HybridAixSpec.kt +6 -0
- package/nitrogen/generated/ios/c++/HybridAixSpecSwift.hpp +7 -0
- package/nitrogen/generated/ios/c++/views/HybridAixComponent.mm +5 -0
- package/nitrogen/generated/ios/swift/HybridAixSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridAixSpec_cxx.swift +17 -0
- package/nitrogen/generated/shared/c++/HybridAixSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridAixSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridAixComponent.cpp +12 -0
- package/nitrogen/generated/shared/c++/views/HybridAixComponent.hpp +1 -0
- package/nitrogen/generated/shared/json/AixConfig.json +1 -0
- package/package.json +1 -1
- package/src/views/aix.nitro.ts +7 -0
package/README.md
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
alt="aix" width="1600" height="900" />
|
|
1
|
+
# aix
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
Primitives for building beautiful AI chat apps with React Native.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
> aix is currently in alpha preview. The API will change, and it is not yet feature complete.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
We're rewriting the engine that powers the chat experience in the v0 mobile app with a focus on native feel.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
- Animate scrolling to new messages when they send
|
|
12
|
-
- Float messages to the top of the screen with automated "blank size" handling
|
|
13
|
-
- Animate message content as it streams
|
|
14
|
-
- Keyboard handling out-of-the-box with no external dependencies
|
|
15
|
-
- Support for absolute-positioned composers
|
|
16
|
-
- Detect "is scrolled near end" for ScrollToEnd buttons
|
|
17
|
-
|
|
18
|
-
To learn about the motivation behind AIX, you can read our blog post on
|
|
19
|
-
[How we built the v0 iOS app](https://vercel.com/blog/how-we-built-the-v0-ios-app).
|
|
20
|
-
AIX is an opinionated, feature-complete, and extensible way to implement every
|
|
21
|
-
single feature mentioned in that blog post.
|
|
22
|
-
|
|
23
|
-
When building AIX, we started by copying the code from v0 into a separate
|
|
24
|
-
repository. However, as we worked to make it flexible for use cases outside of
|
|
25
|
-
our own app, we decided to rewrite it from scratch in native code. What you see
|
|
26
|
-
here is a Nitro Module which handles all business logic in UIKit. We plan on
|
|
27
|
-
adding support for Android as well and welcome contributions.
|
|
28
|
-
|
|
29
|
-
> aix is currently in alpha preview. The API may change.
|
|
9
|
+
aix is a native module with UIKit with Nitro Modules.
|
|
30
10
|
|
|
31
11
|
## Installation
|
|
32
12
|
|
|
@@ -34,195 +14,133 @@ adding support for Android as well and welcome contributions.
|
|
|
34
14
|
npm i aix react-native-nitro-modules
|
|
35
15
|
```
|
|
36
16
|
|
|
37
|
-
Next, rebuild your native app. For Expo users, run `npx expo prebuild` and
|
|
38
|
-
rebuild.
|
|
39
|
-
|
|
40
|
-
- For a full example, see the [example app](./react-native-aix/example/App.tsx).
|
|
17
|
+
Next, rebuild your native app. For Expo users, run `npx expo prebuild` and rebuild.
|
|
41
18
|
|
|
42
19
|
## Usage
|
|
43
20
|
|
|
44
21
|
Wrap your `ScrollView` with `Aix`, and wrap your messages with `AixCell`.
|
|
45
22
|
|
|
23
|
+
<details>
|
|
24
|
+
<summary>Click here to view a full example</summary>
|
|
25
|
+
</details>
|
|
26
|
+
|
|
46
27
|
```tsx
|
|
47
|
-
import { Aix, AixCell } from 'aix'
|
|
48
|
-
import { Message } from 'path/to/your/message'
|
|
49
|
-
import { Composer } from 'path/to/your/composer'
|
|
28
|
+
import { Aix, AixCell } from 'aix';
|
|
29
|
+
import { Message } from 'path/to/your/message';
|
|
30
|
+
import { Composer } from 'path/to/your/composer';
|
|
50
31
|
|
|
51
32
|
export function ChatScreen({ messages }) {
|
|
52
33
|
return (
|
|
53
|
-
<Aix style={{ flex: 1 }}
|
|
34
|
+
<Aix style={{ flex: 1 }}>
|
|
54
35
|
<ScrollView>
|
|
55
36
|
{messages.map((message) => (
|
|
56
|
-
<AixCell
|
|
37
|
+
<AixCell
|
|
38
|
+
key={message.id}
|
|
39
|
+
index={index}
|
|
40
|
+
isLast={index === messages.length - 1}
|
|
41
|
+
>
|
|
57
42
|
<Message message={message} />
|
|
58
43
|
</AixCell>
|
|
59
44
|
))}
|
|
60
45
|
</ScrollView>
|
|
61
46
|
</Aix>
|
|
62
|
-
)
|
|
47
|
+
);
|
|
63
48
|
}
|
|
64
49
|
```
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
To add a floating composer which lets content scroll under it, you can use the
|
|
69
|
-
`AixFooter`. Pair it with `Aix.scrollOnFooterSizeUpdate` to ensure content
|
|
70
|
-
scrolls under the footer when it changes size.
|
|
51
|
+
To add a floating composer which lets content scroll under it, you can use the `AixFooter` and `KeyboardStickyView` from `react-native-keyboard-controller`:
|
|
71
52
|
|
|
72
53
|
```tsx
|
|
73
|
-
import { Aix, AixCell, AixFooter } from 'aix'
|
|
54
|
+
import { Aix, AixCell, AixFooter } from 'aix';
|
|
55
|
+
import { KeyboardStickyView } from 'react-native-keyboard-controller';
|
|
74
56
|
|
|
75
57
|
export function ChatScreen({ messages }) {
|
|
76
|
-
const { bottom } = useSafeAreaInsets()
|
|
77
|
-
|
|
78
58
|
return (
|
|
79
|
-
<Aix
|
|
80
|
-
style={{ flex: 1 }}
|
|
81
|
-
shouldStartAtEnd
|
|
82
|
-
scrollOnFooterSizeUpdate={{
|
|
83
|
-
enabled: true,
|
|
84
|
-
scrolledToEndThreshold: 100,
|
|
85
|
-
animated: false,
|
|
86
|
-
}}
|
|
87
|
-
>
|
|
59
|
+
<Aix style={{ flex: 1 }}>
|
|
88
60
|
<ScrollView>
|
|
89
61
|
{messages.map((message) => (
|
|
90
|
-
<AixCell
|
|
62
|
+
<AixCell
|
|
63
|
+
key={message.id}
|
|
64
|
+
index={index}
|
|
65
|
+
isLast={index === messages.length - 1}
|
|
66
|
+
>
|
|
91
67
|
<Message message={message} />
|
|
92
68
|
</AixCell>
|
|
93
69
|
))}
|
|
94
70
|
</ScrollView>
|
|
95
71
|
|
|
96
|
-
<
|
|
97
|
-
style={{ position: 'absolute', inset: 0, top: 'auto'
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
whenKeyboardOpen: 0,
|
|
102
|
-
whenKeyboardClosed: -bottom,
|
|
103
|
-
},
|
|
104
|
-
}}
|
|
105
|
-
>
|
|
106
|
-
<Composer />
|
|
107
|
-
</AixFooter>
|
|
72
|
+
<KeyboardStickyView offset={{ opened: 0, closed: -bottomInsetPadding }}>
|
|
73
|
+
<AixFooter style={{ position: 'absolute', inset: 0, top: 'auto'}}>
|
|
74
|
+
<Composer />
|
|
75
|
+
</AixFooter>
|
|
76
|
+
</KeyboardStickyView>
|
|
108
77
|
</Aix>
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Send a message
|
|
114
|
-
|
|
115
|
-
When sending a message, you will likely want to scroll to it after it gets added
|
|
116
|
-
to the list.
|
|
117
|
-
|
|
118
|
-
Simply call `aix.current?.scrollToIndexWhenBlankSizeReady(index)` in your submit
|
|
119
|
-
handler.
|
|
120
|
-
|
|
121
|
-
The `index` you pass should correspond to the newest message in the list. For AI
|
|
122
|
-
chats, this is typically the next assistant message index.
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
import { Keyboard } from 'react-native'
|
|
126
|
-
import { useAixRef } from 'aix'
|
|
127
|
-
|
|
128
|
-
function Chat() {
|
|
129
|
-
const aix = useAixRef()
|
|
130
|
-
|
|
131
|
-
const onSubmit = () => {
|
|
132
|
-
aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true)
|
|
133
|
-
requestAnimationFrame(Keyboard.dismiss)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return <Aix ref={aix}>{/* ... */}</Aix>
|
|
78
|
+
);
|
|
137
79
|
}
|
|
138
80
|
```
|
|
139
81
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
You can use `onScrolledNearEndChange` to show a "scroll to end" button when the
|
|
143
|
-
user scrolls away from the bottom:
|
|
144
|
-
|
|
145
|
-
```tsx
|
|
146
|
-
import { Aix, useAixRef } from 'aix'
|
|
147
|
-
import { useState } from 'react'
|
|
148
|
-
import { Button } from 'react-native'
|
|
149
|
-
|
|
150
|
-
function Chat() {
|
|
151
|
-
const aix = useAixRef()
|
|
152
|
-
const [isNearEnd, setIsNearEnd] = useState(false)
|
|
153
|
-
|
|
154
|
-
return (
|
|
155
|
-
<Aix ref={aix} scrollEndReachedThreshold={200} onScrolledNearEndChange={setIsNearEnd}>
|
|
156
|
-
{/* ScrollView and messages... */}
|
|
82
|
+
## TODOs
|
|
157
83
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
</Aix>
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
```
|
|
84
|
+
- [ ] Android support
|
|
85
|
+
- [ ] LegendList support
|
|
86
|
+
- [ ] FlashList support
|
|
165
87
|
|
|
166
88
|
## API Reference
|
|
167
89
|
|
|
168
90
|
### `Aix`
|
|
169
91
|
|
|
170
|
-
The main container component that provides keyboard-aware behavior and manages
|
|
171
|
-
scrolling for chat interfaces.
|
|
92
|
+
The main container component that provides keyboard-aware behavior and manages scrolling for chat interfaces.
|
|
172
93
|
|
|
173
94
|
#### Props
|
|
174
95
|
|
|
175
|
-
| Prop
|
|
176
|
-
|
|
177
|
-
| `shouldStartAtEnd`
|
|
178
|
-
| `scrollOnFooterSizeUpdate`
|
|
179
|
-
| `scrollEndReachedThreshold`
|
|
180
|
-
| `onScrolledNearEndChange`
|
|
181
|
-
| `additionalContentInsets`
|
|
182
|
-
| `additionalScrollIndicatorInsets` | `object`
|
|
183
|
-
| `mainScrollViewID`
|
|
184
|
-
| `penultimateCellIndex`
|
|
96
|
+
| Prop | Type | Default | Description |
|
|
97
|
+
|------|------|---------|-------------|
|
|
98
|
+
| `shouldStartAtEnd` | `boolean` | - | Whether the scroll view should start scrolled to the end of the content. |
|
|
99
|
+
| `scrollOnFooterSizeUpdate` | `object` | `{ enabled: true, scrolledToEndThreshold: 100, animated: false }` | Control the behavior of scrolling when the footer size changes. By default, changing the height of the footer will shift content up in the scroll view. |
|
|
100
|
+
| `scrollEndReachedThreshold` | `number` | `max(blankSize, 200)` | The number of pixels from the bottom of the scroll view to the end of the content that is considered "near the end". Used by `onScrolledNearEndChange` and to determine if content should shift up when keyboard opens. |
|
|
101
|
+
| `onScrolledNearEndChange` | `(isNearEnd: boolean) => void` | - | Callback fired when the scroll position transitions between "near end" and "not near end" states. Reactive to user scrolling, content size changes, parent size changes, and keyboard height changes. Uses `scrollEndReachedThreshold` to determine the threshold. |
|
|
102
|
+
| `additionalContentInsets` | `object` | - | Additional content insets applied when keyboard is open or closed. Shape: `{ top?: { whenKeyboardOpen, whenKeyboardClosed }, bottom?: { whenKeyboardOpen, whenKeyboardClosed } }` |
|
|
103
|
+
| `additionalScrollIndicatorInsets` | `object` | - | Additional insets for the scroll indicator, added to existing safe area insets. Applied to `verticalScrollIndicatorInsets` on iOS. |
|
|
104
|
+
| `mainScrollViewID` | `string` | - | The `nativeID` of the scroll view to use. If provided, will search for a scroll view with this `accessibilityIdentifier`. |
|
|
105
|
+
| `penultimateCellIndex` | `number` | - | The index of the second-to-last message (typically the last user message in AI chat apps). Used to determine which message will be scrolled into view. Useful when you have custom message types like timestamps in your list. |
|
|
185
106
|
|
|
186
107
|
#### Ref Methods
|
|
187
108
|
|
|
188
109
|
Access these methods via `useAixRef()`:
|
|
189
110
|
|
|
190
111
|
```tsx
|
|
191
|
-
const aix = useAixRef()
|
|
112
|
+
const aix = useAixRef();
|
|
192
113
|
|
|
193
114
|
// Scroll to the end of the content
|
|
194
|
-
aix.current?.scrollToEnd(animated)
|
|
115
|
+
aix.current?.scrollToEnd(animated);
|
|
195
116
|
|
|
196
117
|
// Scroll to a specific index when the blank size is ready
|
|
197
|
-
aix.current?.scrollToIndexWhenBlankSizeReady(index, animated, waitForKeyboardToEnd)
|
|
118
|
+
aix.current?.scrollToIndexWhenBlankSizeReady(index, animated, waitForKeyboardToEnd);
|
|
198
119
|
```
|
|
199
120
|
|
|
200
|
-
| Method
|
|
201
|
-
|
|
202
|
-
| `scrollToEnd`
|
|
121
|
+
| Method | Parameters | Description |
|
|
122
|
+
|--------|------------|-------------|
|
|
123
|
+
| `scrollToEnd` | `animated?: boolean` | Scrolls to the end of the content. |
|
|
203
124
|
| `scrollToIndexWhenBlankSizeReady` | `index: number, animated?: boolean, waitForKeyboardToEnd?: boolean` | Scrolls to a specific cell index once the blank size calculation is ready. |
|
|
204
125
|
|
|
205
126
|
---
|
|
206
127
|
|
|
207
128
|
### `AixCell`
|
|
208
129
|
|
|
209
|
-
A wrapper component for each message in the list. It communicates cell position
|
|
210
|
-
and dimensions to the parent `Aix` component.
|
|
130
|
+
A wrapper component for each message in the list. It communicates cell position and dimensions to the parent `Aix` component.
|
|
211
131
|
|
|
212
132
|
#### Props
|
|
213
133
|
|
|
214
|
-
| Prop
|
|
215
|
-
|
|
216
|
-
| `index`
|
|
217
|
-
| `isLast` | `boolean` | Yes
|
|
134
|
+
| Prop | Type | Required | Description |
|
|
135
|
+
|------|------|----------|-------------|
|
|
136
|
+
| `index` | `number` | Yes | The index of this cell in the message list. |
|
|
137
|
+
| `isLast` | `boolean` | Yes | Whether this cell is the last item in the list. Used for scroll positioning and animations. |
|
|
218
138
|
|
|
219
139
|
---
|
|
220
140
|
|
|
221
141
|
### `AixFooter`
|
|
222
142
|
|
|
223
|
-
A footer component for floating composers that allows content to scroll
|
|
224
|
-
underneath it. The footer's height is automatically tracked for proper scroll
|
|
225
|
-
offset calculations.
|
|
143
|
+
A footer component for floating composers that allows content to scroll underneath it. The footer's height is automatically tracked for proper scroll offset calculations.
|
|
226
144
|
|
|
227
145
|
#### Props
|
|
228
146
|
|
|
@@ -230,8 +148,7 @@ Accepts all standard React Native `View` props.
|
|
|
230
148
|
|
|
231
149
|
#### Important Notes
|
|
232
150
|
|
|
233
|
-
- **Do not apply vertical padding** (`padding`, `paddingBottom`) directly to
|
|
234
|
-
`AixFooter`. Apply padding to a child view instead.
|
|
151
|
+
- **Do not apply vertical padding** (`padding`, `paddingBottom`) directly to `AixFooter`. Apply padding to a child view instead.
|
|
235
152
|
- Position the footer absolutely at the bottom of the `Aix` container:
|
|
236
153
|
|
|
237
154
|
```tsx
|
|
@@ -247,32 +164,57 @@ Accepts all standard React Native `View` props.
|
|
|
247
164
|
A hook that returns a ref to access imperative methods on the `Aix` component.
|
|
248
165
|
|
|
249
166
|
```tsx
|
|
250
|
-
import { useAixRef } from 'aix'
|
|
167
|
+
import { useAixRef } from 'aix';
|
|
251
168
|
|
|
252
169
|
function Chat({ messages }) {
|
|
253
|
-
const aix = useAixRef()
|
|
170
|
+
const aix = useAixRef();
|
|
254
171
|
const send = useSendMessage()
|
|
255
|
-
|
|
172
|
+
|
|
256
173
|
const handleSend = () => {
|
|
257
174
|
// Scroll to end after sending a message
|
|
258
|
-
send(message)
|
|
259
|
-
aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true)
|
|
260
|
-
requestAnimationFrame(Keyboard.dismiss)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return <Aix ref={aix}>{/* ... */}</Aix
|
|
175
|
+
send(message);
|
|
176
|
+
aix.current?.scrollToIndexWhenBlankSizeReady(messages.length + 1, true);
|
|
177
|
+
requestAnimationFrame(Keyboard.dismiss);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return <Aix ref={aix}>{/* ... */}</Aix>;
|
|
264
181
|
}
|
|
265
182
|
```
|
|
266
183
|
|
|
267
184
|
---
|
|
268
185
|
|
|
186
|
+
### Scroll to End Button
|
|
187
|
+
|
|
188
|
+
You can use `onScrolledNearEndChange` to show a "scroll to end" button when the user scrolls away from the bottom:
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { Aix, useAixRef } from 'aix';
|
|
192
|
+
import { useState } from 'react';
|
|
193
|
+
|
|
194
|
+
function Chat() {
|
|
195
|
+
const aix = useAixRef();
|
|
196
|
+
const [isNearEnd, setIsNearEnd] = useState(false);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<Aix
|
|
200
|
+
ref={aix}
|
|
201
|
+
scrollEndReachedThreshold={200}
|
|
202
|
+
onScrolledNearEndChange={setIsNearEnd}
|
|
203
|
+
>
|
|
204
|
+
{/* ScrollView and messages... */}
|
|
205
|
+
|
|
206
|
+
{!isNearEnd && (
|
|
207
|
+
<Button onPress={() => aix.current?.scrollToEnd(true)} />
|
|
208
|
+
)}
|
|
209
|
+
</Aix>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
269
214
|
## TODOs
|
|
270
215
|
|
|
271
|
-
- [ ] Android support
|
|
272
|
-
- [ ] LegendList support
|
|
273
|
-
- [ ] FlashList support
|
|
274
216
|
|
|
275
217
|
## Requirements
|
|
276
218
|
|
|
277
219
|
- React Native v0.78.0 or higher
|
|
278
|
-
- Node 18.0.0 or higher
|
|
220
|
+
- Node 18.0.0 or higher
|
|
@@ -5,7 +5,7 @@ import android.view.View
|
|
|
5
5
|
import androidx.annotation.Keep
|
|
6
6
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
7
7
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
-
import com.margelo.nitro.aix
|
|
8
|
+
import com.margelo.nitro.aix.*
|
|
9
9
|
|
|
10
10
|
@Keep
|
|
11
11
|
@DoNotStrip
|
|
@@ -14,14 +14,24 @@ class HybridAix(val context: ThemedReactContext): HybridAixSpec() {
|
|
|
14
14
|
override val view: View = View(context)
|
|
15
15
|
|
|
16
16
|
// Props
|
|
17
|
-
|
|
18
|
-
override var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
override var shouldStartAtEnd: Boolean = true
|
|
18
|
+
override var scrollOnFooterSizeUpdate: AixScrollOnFooterSizeUpdate? = null
|
|
19
|
+
override var scrollEndReachedThreshold: Double? = null
|
|
20
|
+
override var additionalContentInsets: AixAdditionalContentInsetsProp? = null
|
|
21
|
+
override var additionalScrollIndicatorInsets: AixScrollIndicatorInsets? = null
|
|
22
|
+
override var mainScrollViewID: String? = null
|
|
23
|
+
override var penultimateCellIndex: Double? = null
|
|
24
|
+
override var shouldApplyContentInsets: Boolean? = null
|
|
25
|
+
override var applyContentInsetDelay: Double? = null
|
|
26
|
+
override var onWillApplyContentInsets: ((insets: AixContentInsets) -> Unit)? = null
|
|
27
|
+
override var onScrolledNearEndChange: ((isNearEnd: Boolean) -> Unit)? = null
|
|
28
|
+
|
|
29
|
+
// Methods
|
|
30
|
+
override fun scrollToEnd(animated: Boolean?) {
|
|
31
|
+
// TODO: Implement for Android
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override fun scrollToIndexWhenBlankSizeReady(index: Double, animated: Boolean?, waitForKeyboardToEnd: Boolean?) {
|
|
35
|
+
// TODO: Implement for Android
|
|
36
|
+
}
|
|
27
37
|
}
|
package/ios/HybridAix.swift
CHANGED
|
@@ -100,6 +100,7 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
100
100
|
var penultimateCellIndex: Double?
|
|
101
101
|
|
|
102
102
|
var shouldApplyContentInsets: Bool? = nil
|
|
103
|
+
var applyContentInsetDelay: Double? = nil
|
|
103
104
|
var onWillApplyContentInsets: ((_ insets: AixContentInsets) -> Void)? = nil
|
|
104
105
|
var onScrolledNearEndChange: ((_ isNearEnd: Bool) -> Void)? = nil
|
|
105
106
|
|
|
@@ -448,23 +449,32 @@ class HybridAix: HybridAixSpec, AixContext, KeyboardNotificationsDelegate {
|
|
|
448
449
|
)
|
|
449
450
|
|
|
450
451
|
print("[aix] applyContentInset \(targetBottom)")
|
|
452
|
+
onWillApplyContentInsets?(insets)
|
|
451
453
|
|
|
452
454
|
// If shouldApplyContentInsets is explicitly false, call callback and return
|
|
453
455
|
if shouldApplyContentInsets == false {
|
|
454
|
-
onWillApplyContentInsets?(insets)
|
|
455
456
|
return
|
|
456
457
|
}
|
|
457
458
|
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
scrollView
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
459
|
+
// Helper to actually apply the insets
|
|
460
|
+
let applyInsets = { [weak self] in
|
|
461
|
+
guard let self, let scrollView = self.scrollView else { return }
|
|
462
|
+
if scrollView.contentInset.top != targetTop {
|
|
463
|
+
scrollView.contentInset.top = targetTop
|
|
464
|
+
}
|
|
465
|
+
if scrollView.contentInset.bottom != targetBottom {
|
|
466
|
+
scrollView.contentInset.bottom = targetBottom
|
|
467
|
+
}
|
|
468
|
+
// Update scrolled near end state after insets change
|
|
469
|
+
self.updateScrolledNearEndState()
|
|
464
470
|
}
|
|
465
471
|
|
|
466
|
-
//
|
|
467
|
-
|
|
472
|
+
// Apply with optional delay
|
|
473
|
+
if let delay = applyContentInsetDelay, delay > 0 {
|
|
474
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + delay / 1000.0, execute: applyInsets)
|
|
475
|
+
} else {
|
|
476
|
+
applyInsets()
|
|
477
|
+
}
|
|
468
478
|
}
|
|
469
479
|
|
|
470
480
|
/// Centralized function to check and fire onScrolledNearEndChange callback
|
|
@@ -10,6 +10,7 @@ export declare const Aix: import("react").ForwardRefExoticComponent<Omit<{
|
|
|
10
10
|
mainScrollViewID?: string | import("react-native-reanimated").SharedValue<string | undefined> | undefined;
|
|
11
11
|
penultimateCellIndex?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
|
|
12
12
|
shouldApplyContentInsets?: boolean | import("react-native-reanimated").SharedValue<boolean | undefined> | undefined;
|
|
13
|
+
applyContentInsetDelay?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
|
|
13
14
|
onWillApplyContentInsets?: import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | undefined> | undefined;
|
|
14
15
|
onScrolledNearEndChange?: import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | undefined> | undefined;
|
|
15
16
|
hybridRef?: import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | undefined> | undefined;
|
|
@@ -127,6 +128,7 @@ export declare const Aix: import("react").ForwardRefExoticComponent<Omit<{
|
|
|
127
128
|
mainScrollViewID?: string | import("react-native-reanimated").SharedValue<string | undefined> | undefined;
|
|
128
129
|
penultimateCellIndex?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
|
|
129
130
|
shouldApplyContentInsets?: boolean | import("react-native-reanimated").SharedValue<boolean | undefined> | undefined;
|
|
131
|
+
applyContentInsetDelay?: number | import("react-native-reanimated").SharedValue<number | undefined> | undefined;
|
|
130
132
|
onWillApplyContentInsets?: import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((insets: AixContentInsets) => void) | undefined> | undefined> | undefined;
|
|
131
133
|
onScrolledNearEndChange?: import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((isNearEnd: boolean) => void) | undefined> | undefined> | undefined;
|
|
132
134
|
hybridRef?: import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | import("react-native-reanimated").SharedValue<import("react-native-nitro-modules").NitroViewWrappedCallback<((ref: import("react-native-nitro-modules").HybridView<AixProps, AixMethods>) => void) | undefined> | undefined> | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aix.d.ts","sourceRoot":"","sources":["../../../src/aix.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAI/E,MAAM,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;AAgBpD,eAAO,MAAM,GAAG
|
|
1
|
+
{"version":3,"file":"aix.d.ts","sourceRoot":"","sources":["../../../src/aix.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAI/E,MAAM,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;AAgBpD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAJa,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI;8BACnC,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;0CAyCvD,CAAA"}
|
|
@@ -104,6 +104,13 @@ export interface AixProps extends HybridViewProps {
|
|
|
104
104
|
*/
|
|
105
105
|
penultimateCellIndex?: number;
|
|
106
106
|
shouldApplyContentInsets?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Optional delay in milliseconds before applying content insets to the scroll view.
|
|
109
|
+
*
|
|
110
|
+
* This can help work around race conditions where insets are applied before
|
|
111
|
+
* React commits layout changes.
|
|
112
|
+
*/
|
|
113
|
+
applyContentInsetDelay?: number;
|
|
107
114
|
onWillApplyContentInsets?: (insets: AixContentInsets) => void;
|
|
108
115
|
/**
|
|
109
116
|
* Called when the scroll position changes between "near end" and "not near end" states.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aix.nitro.d.ts","sourceRoot":"","sources":["../../../../src/views/aix.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,4BAA4B,CAAA;AAEnC,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,MAAM,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,8BAA8B;IAC7C,GAAG,CAAC,EAAE,0BAA0B,CAAA;IAChC,MAAM,CAAC,EAAE,0BAA0B,CAAA;CACpC;AAED,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,EAAE,MAAM,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,4BAA4B,CAAA;IAClC,MAAM,CAAC,EAAE,4BAA4B,CAAA;CACtC;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,QAAS,SAAQ,eAAe;IAC/C,gBAAgB,EAAE,OAAO,CAAA;IACzB;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,2BAA2B,CAAA;IACtD;;;;;;;;;OASG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC;;;;;;;;;;;;OAYG;IAEH,uBAAuB,CAAC,EAAE,8BAA8B,CAAA;IAExD;;;OAGG;IACH,+BAA+B,CAAC,EAAE,wBAAwB,CAAA;IAE1D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAE7B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAC7D;;;OAGG;IACH,uBAAuB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;CACvD;AAED,MAAM,WAAW,UAAW,SAAQ,iBAAiB;IACnD,WAAW,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACrC,+BAA+B,CAC7B,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,OAAO,EAClB,oBAAoB,CAAC,EAAE,OAAO,GAC7B,IAAI,CAAA;CACR;AAED,MAAM,MAAM,GAAG,GAAG,UAAU,CAC1B,QAAQ,EACR,UAAU,EACV;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CACpC,CAAA;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAClC,gBAAgB,EAChB,EAAE,EACF;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CACpC,CAAA;AAED,MAAM,WAAW,wBAAwB;IACvC,gBAAgB,EAAE,MAAM,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,wBAAwB,CAAA;CAClC;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,eAAe,CAAC,EAAE,kBAAkB,CAAA;CACrC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAClC,gBAAgB,EAChB,EAAE,EACF;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAA;CAAE,CACpC,CAAA"}
|
|
1
|
+
{"version":3,"file":"aix.nitro.d.ts","sourceRoot":"","sources":["../../../../src/views/aix.nitro.ts"],"names":[],"mappings":"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"}
|
|
@@ -140,6 +140,15 @@ namespace margelo::nitro::aix {
|
|
|
140
140
|
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JBoolean> /* shouldApplyContentInsets */)>("setShouldApplyContentInsets");
|
|
141
141
|
method(_javaPart, shouldApplyContentInsets.has_value() ? jni::JBoolean::valueOf(shouldApplyContentInsets.value()) : nullptr);
|
|
142
142
|
}
|
|
143
|
+
std::optional<double> JHybridAixSpec::getApplyContentInsetDelay() {
|
|
144
|
+
static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JDouble>()>("getApplyContentInsetDelay");
|
|
145
|
+
auto __result = method(_javaPart);
|
|
146
|
+
return __result != nullptr ? std::make_optional(__result->value()) : std::nullopt;
|
|
147
|
+
}
|
|
148
|
+
void JHybridAixSpec::setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) {
|
|
149
|
+
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JDouble> /* applyContentInsetDelay */)>("setApplyContentInsetDelay");
|
|
150
|
+
method(_javaPart, applyContentInsetDelay.has_value() ? jni::JDouble::valueOf(applyContentInsetDelay.value()) : nullptr);
|
|
151
|
+
}
|
|
143
152
|
std::optional<std::function<void(const AixContentInsets& /* insets */)>> JHybridAixSpec::getOnWillApplyContentInsets() {
|
|
144
153
|
static const auto method = javaClassStatic()->getMethod<jni::local_ref<JFunc_void_AixContentInsets::javaobject>()>("getOnWillApplyContentInsets_cxx");
|
|
145
154
|
auto __result = method(_javaPart);
|
|
@@ -66,6 +66,8 @@ namespace margelo::nitro::aix {
|
|
|
66
66
|
void setPenultimateCellIndex(std::optional<double> penultimateCellIndex) override;
|
|
67
67
|
std::optional<bool> getShouldApplyContentInsets() override;
|
|
68
68
|
void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) override;
|
|
69
|
+
std::optional<double> getApplyContentInsetDelay() override;
|
|
70
|
+
void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) override;
|
|
69
71
|
std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() override;
|
|
70
72
|
void setOnWillApplyContentInsets(const std::optional<std::function<void(const AixContentInsets& /* insets */)>>& onWillApplyContentInsets) override;
|
|
71
73
|
std::optional<std::function<void(bool /* isNearEnd */)>> getOnScrolledNearEndChange() override;
|
|
@@ -68,6 +68,10 @@ void JHybridAixStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* clas
|
|
|
68
68
|
view->setShouldApplyContentInsets(props.shouldApplyContentInsets.value);
|
|
69
69
|
// TODO: Set isDirty = false
|
|
70
70
|
}
|
|
71
|
+
if (props.applyContentInsetDelay.isDirty) {
|
|
72
|
+
view->setApplyContentInsetDelay(props.applyContentInsetDelay.value);
|
|
73
|
+
// TODO: Set isDirty = false
|
|
74
|
+
}
|
|
71
75
|
if (props.onWillApplyContentInsets.isDirty) {
|
|
72
76
|
view->setOnWillApplyContentInsets(props.onWillApplyContentInsets.value);
|
|
73
77
|
// TODO: Set isDirty = false
|
|
@@ -90,6 +90,12 @@ abstract class HybridAixSpec: HybridView() {
|
|
|
90
90
|
@set:Keep
|
|
91
91
|
abstract var shouldApplyContentInsets: Boolean?
|
|
92
92
|
|
|
93
|
+
@get:DoNotStrip
|
|
94
|
+
@get:Keep
|
|
95
|
+
@set:DoNotStrip
|
|
96
|
+
@set:Keep
|
|
97
|
+
abstract var applyContentInsetDelay: Double?
|
|
98
|
+
|
|
93
99
|
abstract var onWillApplyContentInsets: ((insets: AixContentInsets) -> Unit)?
|
|
94
100
|
|
|
95
101
|
private var onWillApplyContentInsets_cxx: Func_void_AixContentInsets?
|
|
@@ -130,6 +130,13 @@ namespace margelo::nitro::aix {
|
|
|
130
130
|
inline void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) noexcept override {
|
|
131
131
|
_swiftPart.setShouldApplyContentInsets(shouldApplyContentInsets);
|
|
132
132
|
}
|
|
133
|
+
inline std::optional<double> getApplyContentInsetDelay() noexcept override {
|
|
134
|
+
auto __result = _swiftPart.getApplyContentInsetDelay();
|
|
135
|
+
return __result;
|
|
136
|
+
}
|
|
137
|
+
inline void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) noexcept override {
|
|
138
|
+
_swiftPart.setApplyContentInsetDelay(applyContentInsetDelay);
|
|
139
|
+
}
|
|
133
140
|
inline std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() noexcept override {
|
|
134
141
|
auto __result = _swiftPart.getOnWillApplyContentInsets();
|
|
135
142
|
return __result;
|
|
@@ -111,6 +111,11 @@ using namespace margelo::nitro::aix::views;
|
|
|
111
111
|
swiftPart.setShouldApplyContentInsets(newViewProps.shouldApplyContentInsets.value);
|
|
112
112
|
newViewProps.shouldApplyContentInsets.isDirty = false;
|
|
113
113
|
}
|
|
114
|
+
// applyContentInsetDelay: optional
|
|
115
|
+
if (newViewProps.applyContentInsetDelay.isDirty) {
|
|
116
|
+
swiftPart.setApplyContentInsetDelay(newViewProps.applyContentInsetDelay.value);
|
|
117
|
+
newViewProps.applyContentInsetDelay.isDirty = false;
|
|
118
|
+
}
|
|
114
119
|
// onWillApplyContentInsets: optional
|
|
115
120
|
if (newViewProps.onWillApplyContentInsets.isDirty) {
|
|
116
121
|
swiftPart.setOnWillApplyContentInsets(newViewProps.onWillApplyContentInsets.value);
|
|
@@ -19,6 +19,7 @@ public protocol HybridAixSpec_protocol: HybridObject, HybridView {
|
|
|
19
19
|
var mainScrollViewID: String? { get set }
|
|
20
20
|
var penultimateCellIndex: Double? { get set }
|
|
21
21
|
var shouldApplyContentInsets: Bool? { get set }
|
|
22
|
+
var applyContentInsetDelay: Double? { get set }
|
|
22
23
|
var onWillApplyContentInsets: ((_ insets: AixContentInsets) -> Void)? { get set }
|
|
23
24
|
var onScrolledNearEndChange: ((_ isNearEnd: Bool) -> Void)? { get set }
|
|
24
25
|
|
|
@@ -258,6 +258,23 @@ open class HybridAixSpec_cxx {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
public final var applyContentInsetDelay: bridge.std__optional_double_ {
|
|
262
|
+
@inline(__always)
|
|
263
|
+
get {
|
|
264
|
+
return { () -> bridge.std__optional_double_ in
|
|
265
|
+
if let __unwrappedValue = self.__implementation.applyContentInsetDelay {
|
|
266
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
267
|
+
} else {
|
|
268
|
+
return .init()
|
|
269
|
+
}
|
|
270
|
+
}()
|
|
271
|
+
}
|
|
272
|
+
@inline(__always)
|
|
273
|
+
set {
|
|
274
|
+
self.__implementation.applyContentInsetDelay = newValue.value
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
261
278
|
public final var onWillApplyContentInsets: bridge.std__optional_std__function_void_const_AixContentInsets_____insets______ {
|
|
262
279
|
@inline(__always)
|
|
263
280
|
get {
|
|
@@ -30,6 +30,8 @@ namespace margelo::nitro::aix {
|
|
|
30
30
|
prototype.registerHybridSetter("penultimateCellIndex", &HybridAixSpec::setPenultimateCellIndex);
|
|
31
31
|
prototype.registerHybridGetter("shouldApplyContentInsets", &HybridAixSpec::getShouldApplyContentInsets);
|
|
32
32
|
prototype.registerHybridSetter("shouldApplyContentInsets", &HybridAixSpec::setShouldApplyContentInsets);
|
|
33
|
+
prototype.registerHybridGetter("applyContentInsetDelay", &HybridAixSpec::getApplyContentInsetDelay);
|
|
34
|
+
prototype.registerHybridSetter("applyContentInsetDelay", &HybridAixSpec::setApplyContentInsetDelay);
|
|
33
35
|
prototype.registerHybridGetter("onWillApplyContentInsets", &HybridAixSpec::getOnWillApplyContentInsets);
|
|
34
36
|
prototype.registerHybridSetter("onWillApplyContentInsets", &HybridAixSpec::setOnWillApplyContentInsets);
|
|
35
37
|
prototype.registerHybridGetter("onScrolledNearEndChange", &HybridAixSpec::getOnScrolledNearEndChange);
|
|
@@ -73,6 +73,8 @@ namespace margelo::nitro::aix {
|
|
|
73
73
|
virtual void setPenultimateCellIndex(std::optional<double> penultimateCellIndex) = 0;
|
|
74
74
|
virtual std::optional<bool> getShouldApplyContentInsets() = 0;
|
|
75
75
|
virtual void setShouldApplyContentInsets(std::optional<bool> shouldApplyContentInsets) = 0;
|
|
76
|
+
virtual std::optional<double> getApplyContentInsetDelay() = 0;
|
|
77
|
+
virtual void setApplyContentInsetDelay(std::optional<double> applyContentInsetDelay) = 0;
|
|
76
78
|
virtual std::optional<std::function<void(const AixContentInsets& /* insets */)>> getOnWillApplyContentInsets() = 0;
|
|
77
79
|
virtual void setOnWillApplyContentInsets(const std::optional<std::function<void(const AixContentInsets& /* insets */)>>& onWillApplyContentInsets) = 0;
|
|
78
80
|
virtual std::optional<std::function<void(bool /* isNearEnd */)>> getOnScrolledNearEndChange() = 0;
|
|
@@ -105,6 +105,16 @@ namespace margelo::nitro::aix::views {
|
|
|
105
105
|
throw std::runtime_error(std::string("Aix.shouldApplyContentInsets: ") + exc.what());
|
|
106
106
|
}
|
|
107
107
|
}()),
|
|
108
|
+
applyContentInsetDelay([&]() -> CachedProp<std::optional<double>> {
|
|
109
|
+
try {
|
|
110
|
+
const react::RawValue* rawValue = rawProps.at("applyContentInsetDelay", nullptr, nullptr);
|
|
111
|
+
if (rawValue == nullptr) return sourceProps.applyContentInsetDelay;
|
|
112
|
+
const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
|
|
113
|
+
return CachedProp<std::optional<double>>::fromRawValue(*runtime, value, sourceProps.applyContentInsetDelay);
|
|
114
|
+
} catch (const std::exception& exc) {
|
|
115
|
+
throw std::runtime_error(std::string("Aix.applyContentInsetDelay: ") + exc.what());
|
|
116
|
+
}
|
|
117
|
+
}()),
|
|
108
118
|
onWillApplyContentInsets([&]() -> CachedProp<std::optional<std::function<void(const AixContentInsets& /* insets */)>>> {
|
|
109
119
|
try {
|
|
110
120
|
const react::RawValue* rawValue = rawProps.at("onWillApplyContentInsets", nullptr, nullptr);
|
|
@@ -146,6 +156,7 @@ namespace margelo::nitro::aix::views {
|
|
|
146
156
|
mainScrollViewID(other.mainScrollViewID),
|
|
147
157
|
penultimateCellIndex(other.penultimateCellIndex),
|
|
148
158
|
shouldApplyContentInsets(other.shouldApplyContentInsets),
|
|
159
|
+
applyContentInsetDelay(other.applyContentInsetDelay),
|
|
149
160
|
onWillApplyContentInsets(other.onWillApplyContentInsets),
|
|
150
161
|
onScrolledNearEndChange(other.onScrolledNearEndChange),
|
|
151
162
|
hybridRef(other.hybridRef) { }
|
|
@@ -160,6 +171,7 @@ namespace margelo::nitro::aix::views {
|
|
|
160
171
|
case hashString("mainScrollViewID"): return true;
|
|
161
172
|
case hashString("penultimateCellIndex"): return true;
|
|
162
173
|
case hashString("shouldApplyContentInsets"): return true;
|
|
174
|
+
case hashString("applyContentInsetDelay"): return true;
|
|
163
175
|
case hashString("onWillApplyContentInsets"): return true;
|
|
164
176
|
case hashString("onScrolledNearEndChange"): return true;
|
|
165
177
|
case hashString("hybridRef"): return true;
|
|
@@ -55,6 +55,7 @@ namespace margelo::nitro::aix::views {
|
|
|
55
55
|
CachedProp<std::optional<std::string>> mainScrollViewID;
|
|
56
56
|
CachedProp<std::optional<double>> penultimateCellIndex;
|
|
57
57
|
CachedProp<std::optional<bool>> shouldApplyContentInsets;
|
|
58
|
+
CachedProp<std::optional<double>> applyContentInsetDelay;
|
|
58
59
|
CachedProp<std::optional<std::function<void(const AixContentInsets& /* insets */)>>> onWillApplyContentInsets;
|
|
59
60
|
CachedProp<std::optional<std::function<void(bool /* isNearEnd */)>>> onScrolledNearEndChange;
|
|
60
61
|
CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridAixSpec>& /* ref */)>>> hybridRef;
|
package/package.json
CHANGED
package/src/views/aix.nitro.ts
CHANGED
|
@@ -120,6 +120,13 @@ export interface AixProps extends HybridViewProps {
|
|
|
120
120
|
penultimateCellIndex?: number
|
|
121
121
|
|
|
122
122
|
shouldApplyContentInsets?: boolean
|
|
123
|
+
/**
|
|
124
|
+
* Optional delay in milliseconds before applying content insets to the scroll view.
|
|
125
|
+
*
|
|
126
|
+
* This can help work around race conditions where insets are applied before
|
|
127
|
+
* React commits layout changes.
|
|
128
|
+
*/
|
|
129
|
+
applyContentInsetDelay?: number
|
|
123
130
|
onWillApplyContentInsets?: (insets: AixContentInsets) => void
|
|
124
131
|
/**
|
|
125
132
|
* Called when the scroll position changes between "near end" and "not near end" states.
|