stream-chat-react-native-core 9.6.1-beta.5 → 9.6.1-beta.8
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/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js +22 -2
- package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/commonjs/components/MessageInput/MessageComposer.js +2 -2
- package/lib/commonjs/components/MessageInput/MessageComposer.js.map +1 -1
- package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js +61 -11
- package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js +22 -2
- package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
- package/lib/module/components/MessageInput/MessageComposer.js +2 -2
- package/lib/module/components/MessageInput/MessageComposer.js.map +1 -1
- package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js +61 -11
- package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/MessageComposer.d.ts.map +1 -1
- package/lib/typescript/contexts/overlayContext/MessageOverlayHostLayer.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx +58 -3
- package/src/components/MessageInput/MessageComposer.tsx +2 -3
- package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.tsx.snap +36 -36
- package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.tsx.snap +19 -19
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.tsx.snap +6 -6
- package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +92 -12
- package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx +62 -6
- package/src/version.json +1 -1
|
@@ -38,9 +38,9 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
38
38
|
<View
|
|
39
39
|
handlerTags={
|
|
40
40
|
[
|
|
41
|
+
37,
|
|
42
|
+
36,
|
|
41
43
|
35,
|
|
42
|
-
34,
|
|
43
|
-
33,
|
|
44
44
|
]
|
|
45
45
|
}
|
|
46
46
|
onGestureHandlerEvent={[Function]}
|
|
@@ -212,9 +212,9 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
212
212
|
<View
|
|
213
213
|
handlerTags={
|
|
214
214
|
[
|
|
215
|
+
40,
|
|
216
|
+
39,
|
|
215
217
|
38,
|
|
216
|
-
37,
|
|
217
|
-
36,
|
|
218
218
|
]
|
|
219
219
|
}
|
|
220
220
|
onGestureHandlerEvent={[Function]}
|
|
@@ -359,9 +359,9 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
359
359
|
<View
|
|
360
360
|
handlerTags={
|
|
361
361
|
[
|
|
362
|
+
43,
|
|
363
|
+
42,
|
|
362
364
|
41,
|
|
363
|
-
40,
|
|
364
|
-
39,
|
|
365
365
|
]
|
|
366
366
|
}
|
|
367
367
|
onGestureHandlerEvent={[Function]}
|
|
@@ -535,9 +535,9 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
535
535
|
<View
|
|
536
536
|
handlerTags={
|
|
537
537
|
[
|
|
538
|
+
46,
|
|
539
|
+
45,
|
|
538
540
|
44,
|
|
539
|
-
43,
|
|
540
|
-
42,
|
|
541
541
|
]
|
|
542
542
|
}
|
|
543
543
|
onGestureHandlerEvent={[Function]}
|
|
@@ -678,9 +678,9 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
678
678
|
<View
|
|
679
679
|
handlerTags={
|
|
680
680
|
[
|
|
681
|
+
49,
|
|
682
|
+
48,
|
|
681
683
|
47,
|
|
682
|
-
46,
|
|
683
|
-
45,
|
|
684
684
|
]
|
|
685
685
|
}
|
|
686
686
|
onGestureHandlerEvent={[Function]}
|
|
@@ -844,17 +844,16 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
844
844
|
}
|
|
845
845
|
>
|
|
846
846
|
<View
|
|
847
|
+
collapsable={false}
|
|
847
848
|
style={
|
|
848
849
|
{
|
|
849
850
|
"height": 0,
|
|
850
|
-
"opacity": 0,
|
|
851
|
-
"width": 0,
|
|
852
851
|
}
|
|
853
852
|
}
|
|
854
|
-
testID="message-overlay-
|
|
853
|
+
testID="message-overlay-message"
|
|
855
854
|
>
|
|
856
855
|
<View
|
|
857
|
-
name="
|
|
856
|
+
name="message-overlay"
|
|
858
857
|
style={
|
|
859
858
|
{
|
|
860
859
|
"bottom": 0,
|
|
@@ -867,16 +866,17 @@ exports[`AttachButton should call handleAttachButtonPress when the button is cli
|
|
|
867
866
|
/>
|
|
868
867
|
</View>
|
|
869
868
|
<View
|
|
870
|
-
pointerEvents="box-none"
|
|
871
869
|
style={
|
|
872
870
|
{
|
|
873
871
|
"height": 0,
|
|
872
|
+
"opacity": 0,
|
|
873
|
+
"width": 0,
|
|
874
874
|
}
|
|
875
875
|
}
|
|
876
|
-
testID="message-overlay-
|
|
876
|
+
testID="message-overlay-top"
|
|
877
877
|
>
|
|
878
878
|
<View
|
|
879
|
-
name="
|
|
879
|
+
name="top-item"
|
|
880
880
|
style={
|
|
881
881
|
{
|
|
882
882
|
"bottom": 0,
|
|
@@ -954,9 +954,9 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
954
954
|
<View
|
|
955
955
|
handlerTags={
|
|
956
956
|
[
|
|
957
|
+
20,
|
|
957
958
|
19,
|
|
958
959
|
18,
|
|
959
|
-
17,
|
|
960
960
|
]
|
|
961
961
|
}
|
|
962
962
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1128,9 +1128,9 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1128
1128
|
<View
|
|
1129
1129
|
handlerTags={
|
|
1130
1130
|
[
|
|
1131
|
+
23,
|
|
1131
1132
|
22,
|
|
1132
1133
|
21,
|
|
1133
|
-
20,
|
|
1134
1134
|
]
|
|
1135
1135
|
}
|
|
1136
1136
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1275,9 +1275,9 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1275
1275
|
<View
|
|
1276
1276
|
handlerTags={
|
|
1277
1277
|
[
|
|
1278
|
+
26,
|
|
1278
1279
|
25,
|
|
1279
1280
|
24,
|
|
1280
|
-
23,
|
|
1281
1281
|
]
|
|
1282
1282
|
}
|
|
1283
1283
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1451,9 +1451,9 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1451
1451
|
<View
|
|
1452
1452
|
handlerTags={
|
|
1453
1453
|
[
|
|
1454
|
+
29,
|
|
1454
1455
|
28,
|
|
1455
1456
|
27,
|
|
1456
|
-
26,
|
|
1457
1457
|
]
|
|
1458
1458
|
}
|
|
1459
1459
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1594,9 +1594,9 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1594
1594
|
<View
|
|
1595
1595
|
handlerTags={
|
|
1596
1596
|
[
|
|
1597
|
+
32,
|
|
1597
1598
|
31,
|
|
1598
1599
|
30,
|
|
1599
|
-
29,
|
|
1600
1600
|
]
|
|
1601
1601
|
}
|
|
1602
1602
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1760,17 +1760,16 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1760
1760
|
}
|
|
1761
1761
|
>
|
|
1762
1762
|
<View
|
|
1763
|
+
collapsable={false}
|
|
1763
1764
|
style={
|
|
1764
1765
|
{
|
|
1765
1766
|
"height": 0,
|
|
1766
|
-
"opacity": 0,
|
|
1767
|
-
"width": 0,
|
|
1768
1767
|
}
|
|
1769
1768
|
}
|
|
1770
|
-
testID="message-overlay-
|
|
1769
|
+
testID="message-overlay-message"
|
|
1771
1770
|
>
|
|
1772
1771
|
<View
|
|
1773
|
-
name="
|
|
1772
|
+
name="message-overlay"
|
|
1774
1773
|
style={
|
|
1775
1774
|
{
|
|
1776
1775
|
"bottom": 0,
|
|
@@ -1783,16 +1782,17 @@ exports[`AttachButton should render a enabled AttachButton 1`] = `
|
|
|
1783
1782
|
/>
|
|
1784
1783
|
</View>
|
|
1785
1784
|
<View
|
|
1786
|
-
pointerEvents="box-none"
|
|
1787
1785
|
style={
|
|
1788
1786
|
{
|
|
1789
1787
|
"height": 0,
|
|
1788
|
+
"opacity": 0,
|
|
1789
|
+
"width": 0,
|
|
1790
1790
|
}
|
|
1791
1791
|
}
|
|
1792
|
-
testID="message-overlay-
|
|
1792
|
+
testID="message-overlay-top"
|
|
1793
1793
|
>
|
|
1794
1794
|
<View
|
|
1795
|
-
name="
|
|
1795
|
+
name="top-item"
|
|
1796
1796
|
style={
|
|
1797
1797
|
{
|
|
1798
1798
|
"bottom": 0,
|
|
@@ -2676,17 +2676,16 @@ exports[`AttachButton should render an disabled AttachButton 1`] = `
|
|
|
2676
2676
|
}
|
|
2677
2677
|
>
|
|
2678
2678
|
<View
|
|
2679
|
+
collapsable={false}
|
|
2679
2680
|
style={
|
|
2680
2681
|
{
|
|
2681
2682
|
"height": 0,
|
|
2682
|
-
"opacity": 0,
|
|
2683
|
-
"width": 0,
|
|
2684
2683
|
}
|
|
2685
2684
|
}
|
|
2686
|
-
testID="message-overlay-
|
|
2685
|
+
testID="message-overlay-message"
|
|
2687
2686
|
>
|
|
2688
2687
|
<View
|
|
2689
|
-
name="
|
|
2688
|
+
name="message-overlay"
|
|
2690
2689
|
style={
|
|
2691
2690
|
{
|
|
2692
2691
|
"bottom": 0,
|
|
@@ -2699,16 +2698,17 @@ exports[`AttachButton should render an disabled AttachButton 1`] = `
|
|
|
2699
2698
|
/>
|
|
2700
2699
|
</View>
|
|
2701
2700
|
<View
|
|
2702
|
-
pointerEvents="box-none"
|
|
2703
2701
|
style={
|
|
2704
2702
|
{
|
|
2705
2703
|
"height": 0,
|
|
2704
|
+
"opacity": 0,
|
|
2705
|
+
"width": 0,
|
|
2706
2706
|
}
|
|
2707
2707
|
}
|
|
2708
|
-
testID="message-overlay-
|
|
2708
|
+
testID="message-overlay-top"
|
|
2709
2709
|
>
|
|
2710
2710
|
<View
|
|
2711
|
-
name="
|
|
2711
|
+
name="top-item"
|
|
2712
2712
|
style={
|
|
2713
2713
|
{
|
|
2714
2714
|
"bottom": 0,
|
|
@@ -842,17 +842,16 @@ exports[`SendButton should render a SendButton 1`] = `
|
|
|
842
842
|
}
|
|
843
843
|
>
|
|
844
844
|
<View
|
|
845
|
+
collapsable={false}
|
|
845
846
|
style={
|
|
846
847
|
{
|
|
847
848
|
"height": 0,
|
|
848
|
-
"opacity": 0,
|
|
849
|
-
"width": 0,
|
|
850
849
|
}
|
|
851
850
|
}
|
|
852
|
-
testID="message-overlay-
|
|
851
|
+
testID="message-overlay-message"
|
|
853
852
|
>
|
|
854
853
|
<View
|
|
855
|
-
name="
|
|
854
|
+
name="message-overlay"
|
|
856
855
|
style={
|
|
857
856
|
{
|
|
858
857
|
"bottom": 0,
|
|
@@ -865,16 +864,17 @@ exports[`SendButton should render a SendButton 1`] = `
|
|
|
865
864
|
/>
|
|
866
865
|
</View>
|
|
867
866
|
<View
|
|
868
|
-
pointerEvents="box-none"
|
|
869
867
|
style={
|
|
870
868
|
{
|
|
871
869
|
"height": 0,
|
|
870
|
+
"opacity": 0,
|
|
871
|
+
"width": 0,
|
|
872
872
|
}
|
|
873
873
|
}
|
|
874
|
-
testID="message-overlay-
|
|
874
|
+
testID="message-overlay-top"
|
|
875
875
|
>
|
|
876
876
|
<View
|
|
877
|
-
name="
|
|
877
|
+
name="top-item"
|
|
878
878
|
style={
|
|
879
879
|
{
|
|
880
880
|
"bottom": 0,
|
|
@@ -952,9 +952,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
952
952
|
<View
|
|
953
953
|
handlerTags={
|
|
954
954
|
[
|
|
955
|
+
20,
|
|
955
956
|
19,
|
|
956
957
|
18,
|
|
957
|
-
17,
|
|
958
958
|
]
|
|
959
959
|
}
|
|
960
960
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1124,9 +1124,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1124
1124
|
<View
|
|
1125
1125
|
handlerTags={
|
|
1126
1126
|
[
|
|
1127
|
+
23,
|
|
1127
1128
|
22,
|
|
1128
1129
|
21,
|
|
1129
|
-
20,
|
|
1130
1130
|
]
|
|
1131
1131
|
}
|
|
1132
1132
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1271,9 +1271,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1271
1271
|
<View
|
|
1272
1272
|
handlerTags={
|
|
1273
1273
|
[
|
|
1274
|
+
26,
|
|
1274
1275
|
25,
|
|
1275
1276
|
24,
|
|
1276
|
-
23,
|
|
1277
1277
|
]
|
|
1278
1278
|
}
|
|
1279
1279
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1447,9 +1447,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1447
1447
|
<View
|
|
1448
1448
|
handlerTags={
|
|
1449
1449
|
[
|
|
1450
|
+
29,
|
|
1450
1451
|
28,
|
|
1451
1452
|
27,
|
|
1452
|
-
26,
|
|
1453
1453
|
]
|
|
1454
1454
|
}
|
|
1455
1455
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1590,9 +1590,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1590
1590
|
<View
|
|
1591
1591
|
handlerTags={
|
|
1592
1592
|
[
|
|
1593
|
+
32,
|
|
1593
1594
|
31,
|
|
1594
1595
|
30,
|
|
1595
|
-
29,
|
|
1596
1596
|
]
|
|
1597
1597
|
}
|
|
1598
1598
|
onGestureHandlerEvent={[Function]}
|
|
@@ -1756,17 +1756,16 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1756
1756
|
}
|
|
1757
1757
|
>
|
|
1758
1758
|
<View
|
|
1759
|
+
collapsable={false}
|
|
1759
1760
|
style={
|
|
1760
1761
|
{
|
|
1761
1762
|
"height": 0,
|
|
1762
|
-
"opacity": 0,
|
|
1763
|
-
"width": 0,
|
|
1764
1763
|
}
|
|
1765
1764
|
}
|
|
1766
|
-
testID="message-overlay-
|
|
1765
|
+
testID="message-overlay-message"
|
|
1767
1766
|
>
|
|
1768
1767
|
<View
|
|
1769
|
-
name="
|
|
1768
|
+
name="message-overlay"
|
|
1770
1769
|
style={
|
|
1771
1770
|
{
|
|
1772
1771
|
"bottom": 0,
|
|
@@ -1779,16 +1778,17 @@ exports[`SendButton should render a disabled SendButton 1`] = `
|
|
|
1779
1778
|
/>
|
|
1780
1779
|
</View>
|
|
1781
1780
|
<View
|
|
1782
|
-
pointerEvents="box-none"
|
|
1783
1781
|
style={
|
|
1784
1782
|
{
|
|
1785
1783
|
"height": 0,
|
|
1784
|
+
"opacity": 0,
|
|
1785
|
+
"width": 0,
|
|
1786
1786
|
}
|
|
1787
1787
|
}
|
|
1788
|
-
testID="message-overlay-
|
|
1788
|
+
testID="message-overlay-top"
|
|
1789
1789
|
>
|
|
1790
1790
|
<View
|
|
1791
|
-
name="
|
|
1791
|
+
name="top-item"
|
|
1792
1792
|
style={
|
|
1793
1793
|
{
|
|
1794
1794
|
"bottom": 0,
|
|
@@ -1833,9 +1833,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
1833
1833
|
<View
|
|
1834
1834
|
handlerTags={
|
|
1835
1835
|
[
|
|
1836
|
+
27,
|
|
1836
1837
|
26,
|
|
1837
1838
|
25,
|
|
1838
|
-
24,
|
|
1839
1839
|
]
|
|
1840
1840
|
}
|
|
1841
1841
|
onGestureHandlerEvent={[Function]}
|
|
@@ -2211,9 +2211,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
2211
2211
|
<View
|
|
2212
2212
|
handlerTags={
|
|
2213
2213
|
[
|
|
2214
|
+
30,
|
|
2214
2215
|
29,
|
|
2215
2216
|
28,
|
|
2216
|
-
27,
|
|
2217
2217
|
]
|
|
2218
2218
|
}
|
|
2219
2219
|
onGestureHandlerEvent={[Function]}
|
|
@@ -2393,9 +2393,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
2393
2393
|
<View
|
|
2394
2394
|
handlerTags={
|
|
2395
2395
|
[
|
|
2396
|
+
33,
|
|
2396
2397
|
32,
|
|
2397
2398
|
31,
|
|
2398
|
-
30,
|
|
2399
2399
|
]
|
|
2400
2400
|
}
|
|
2401
2401
|
onGestureHandlerEvent={[Function]}
|
|
@@ -2540,9 +2540,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
2540
2540
|
<View
|
|
2541
2541
|
handlerTags={
|
|
2542
2542
|
[
|
|
2543
|
+
36,
|
|
2543
2544
|
35,
|
|
2544
2545
|
34,
|
|
2545
|
-
33,
|
|
2546
2546
|
]
|
|
2547
2547
|
}
|
|
2548
2548
|
onGestureHandlerEvent={[Function]}
|
|
@@ -2716,9 +2716,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
2716
2716
|
<View
|
|
2717
2717
|
handlerTags={
|
|
2718
2718
|
[
|
|
2719
|
+
39,
|
|
2719
2720
|
38,
|
|
2720
2721
|
37,
|
|
2721
|
-
36,
|
|
2722
2722
|
]
|
|
2723
2723
|
}
|
|
2724
2724
|
onGestureHandlerEvent={[Function]}
|
|
@@ -2859,9 +2859,9 @@ exports[`Thread should match thread snapshot 1`] = `
|
|
|
2859
2859
|
<View
|
|
2860
2860
|
handlerTags={
|
|
2861
2861
|
[
|
|
2862
|
+
42,
|
|
2862
2863
|
41,
|
|
2863
2864
|
40,
|
|
2864
|
-
39,
|
|
2865
2865
|
]
|
|
2866
2866
|
}
|
|
2867
2867
|
onGestureHandlerEvent={[Function]}
|
|
@@ -12,11 +12,13 @@ import {
|
|
|
12
12
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
13
13
|
import Animated, {
|
|
14
14
|
type AnimatedStyle,
|
|
15
|
+
cancelAnimation,
|
|
15
16
|
clamp,
|
|
16
17
|
runOnJS,
|
|
17
18
|
useAnimatedStyle,
|
|
18
19
|
useDerivedValue,
|
|
19
20
|
useSharedValue,
|
|
21
|
+
withDecay,
|
|
20
22
|
withSpring,
|
|
21
23
|
} from 'react-native-reanimated';
|
|
22
24
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
@@ -118,6 +120,41 @@ export const MessageOverlayHostLayer = () => {
|
|
|
118
120
|
const minY = topInset + padding;
|
|
119
121
|
const maxY = screenH - bottomInset - padding;
|
|
120
122
|
|
|
123
|
+
// When top + message + bottom don't fit the usable area, a Pan gesture drives `scrollY` and
|
|
124
|
+
// the message is translated by it (see hostStyle). No ScrollView, all on the UI thread.
|
|
125
|
+
const scrollY = useSharedValue(0);
|
|
126
|
+
const scrollStartY = useSharedValue(0);
|
|
127
|
+
|
|
128
|
+
const scrollGeom = useDerivedValue(() => {
|
|
129
|
+
if (!messageH.value || !topH.value || !bottomH.value) {
|
|
130
|
+
return { needsScroll: false };
|
|
131
|
+
}
|
|
132
|
+
const available = maxY - minY;
|
|
133
|
+
const contentH = topH.value.h + messageH.value.h + bottomH.value.h;
|
|
134
|
+
return { needsScroll: contentH > available };
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// How far the message can scroll: the amount its content overflows the usable area. At max
|
|
138
|
+
// scroll the message tail lands just above the sticky footer.
|
|
139
|
+
const maxScroll = useDerivedValue(() => {
|
|
140
|
+
if (!scrollGeom.value.needsScroll || !messageH.value || !topH.value || !bottomH.value) {
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
const available = maxY - minY;
|
|
144
|
+
const contentH = topH.value.h + messageH.value.h + bottomH.value.h;
|
|
145
|
+
return Math.max(0, contentH - available);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// On close, freeze any inflight fling (the message closes from where it was scrolled - see
|
|
149
|
+
// `hostStyle`) and only reset the offset once fully closed, ready for the next open.
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
if (closing) {
|
|
152
|
+
cancelAnimation(scrollY);
|
|
153
|
+
} else if (!isActive) {
|
|
154
|
+
scrollY.value = 0;
|
|
155
|
+
}
|
|
156
|
+
}, [closing, isActive, scrollY]);
|
|
157
|
+
|
|
121
158
|
const backdrop = useSharedValue(0);
|
|
122
159
|
const closeCoverOpacity = useSharedValue(0);
|
|
123
160
|
|
|
@@ -171,6 +208,12 @@ export const MessageOverlayHostLayer = () => {
|
|
|
171
208
|
const anchorY = messageH.value.y;
|
|
172
209
|
const msgH = messageH.value.h;
|
|
173
210
|
const minTop = minY + topH.value.h;
|
|
211
|
+
|
|
212
|
+
// When scrolling, pin the message directly under the (pinned) top item; scrollY does the rest.
|
|
213
|
+
if (scrollGeom.value.needsScroll) {
|
|
214
|
+
return minTop - anchorY;
|
|
215
|
+
}
|
|
216
|
+
|
|
174
217
|
const maxTopWithBottom = maxY - (msgH + bottomH.value.h);
|
|
175
218
|
const canFitBottomWithoutOverlap = minTop <= maxTopWithBottom;
|
|
176
219
|
const solvedTop = canFitBottomWithoutOverlap
|
|
@@ -186,6 +229,12 @@ export const MessageOverlayHostLayer = () => {
|
|
|
186
229
|
const anchorMessageTop = messageH.value.y;
|
|
187
230
|
const msgH = messageH.value.h;
|
|
188
231
|
const minMessageTop = minY + topH.value.h;
|
|
232
|
+
|
|
233
|
+
// When scrolling, pin the bottom item as a sticky footer at the bottom of the usable area.
|
|
234
|
+
if (scrollGeom.value.needsScroll) {
|
|
235
|
+
return maxY - bottomH.value.h - bottomH.value.y;
|
|
236
|
+
}
|
|
237
|
+
|
|
189
238
|
const maxMessageTopWithBottom = maxY - (msgH + bottomH.value.h);
|
|
190
239
|
const canFitBottomWithoutOverlap = minMessageTop <= maxMessageTopWithBottom;
|
|
191
240
|
const solvedMessageTop = canFitBottomWithoutOverlap
|
|
@@ -234,19 +283,47 @@ export const MessageOverlayHostLayer = () => {
|
|
|
234
283
|
};
|
|
235
284
|
});
|
|
236
285
|
|
|
286
|
+
// Two composed `translateY`: first is the springed open/close position, second is the raw
|
|
287
|
+
// scroll offset. On close we fold the scroll into the first (springed) target so the message
|
|
288
|
+
// animates back from where it was scrolled, while the latter stays put.
|
|
237
289
|
const hostStyle = useAnimatedStyle(() => {
|
|
238
290
|
if (!messageH.value) return { height: 0 };
|
|
239
|
-
const
|
|
291
|
+
const scroll = scrollGeom.value.needsScroll ? scrollY.value : 0;
|
|
292
|
+
const translateY = isActive
|
|
293
|
+
? closing
|
|
294
|
+
? closeCorrectionY.value + scroll
|
|
295
|
+
: messageShiftY.value
|
|
296
|
+
: 0;
|
|
240
297
|
return {
|
|
241
298
|
height: messageH.value.h,
|
|
242
299
|
left: messageH.value.x,
|
|
243
300
|
position: 'absolute',
|
|
244
301
|
top: messageH.value.y,
|
|
245
|
-
transform: [
|
|
302
|
+
transform: [
|
|
303
|
+
{ translateY: withSpring(translateY, { duration: DURATION }) },
|
|
304
|
+
{ translateY: scroll === 0 ? 0 : -scroll },
|
|
305
|
+
],
|
|
246
306
|
width: messageH.value.w,
|
|
247
307
|
};
|
|
248
308
|
});
|
|
249
309
|
|
|
310
|
+
// Drag scrolls the message (clamped to `[0, maxScroll]`); release flings with decay. When the
|
|
311
|
+
// content fits, `maxScroll` is 0 so this is a noop.
|
|
312
|
+
const pan = useMemo(
|
|
313
|
+
() =>
|
|
314
|
+
Gesture.Pan()
|
|
315
|
+
.onBegin(() => {
|
|
316
|
+
scrollStartY.value = scrollY.value;
|
|
317
|
+
})
|
|
318
|
+
.onUpdate((e) => {
|
|
319
|
+
scrollY.value = clamp(scrollStartY.value - e.translationY, 0, maxScroll.value);
|
|
320
|
+
})
|
|
321
|
+
.onEnd((e) => {
|
|
322
|
+
scrollY.value = withDecay({ clamp: [0, maxScroll.value], velocity: -e.velocityY });
|
|
323
|
+
}),
|
|
324
|
+
[maxScroll, scrollStartY, scrollY],
|
|
325
|
+
);
|
|
326
|
+
|
|
250
327
|
const tap = useMemo(
|
|
251
328
|
() =>
|
|
252
329
|
Gesture.Tap()
|
|
@@ -279,8 +356,9 @@ export const MessageOverlayHostLayer = () => {
|
|
|
279
356
|
})
|
|
280
357
|
.onEnd(() => {
|
|
281
358
|
runOnJS(closeOverlay)();
|
|
282
|
-
})
|
|
283
|
-
|
|
359
|
+
})
|
|
360
|
+
.requireExternalGestureToFail(pan),
|
|
361
|
+
[bottomH, bottomShiftY, messageShiftY, pan, topH],
|
|
284
362
|
);
|
|
285
363
|
|
|
286
364
|
return (
|
|
@@ -314,18 +392,20 @@ export const MessageOverlayHostLayer = () => {
|
|
|
314
392
|
/>
|
|
315
393
|
) : (
|
|
316
394
|
<>
|
|
395
|
+
{/*
|
|
396
|
+
Message renders first (lowest z) so it sits behind the opaque top/bottom items;
|
|
397
|
+
a scrolled/overflowing message passes behind them instead of being clipped.
|
|
398
|
+
*/}
|
|
399
|
+
<GestureDetector gesture={pan}>
|
|
400
|
+
<Animated.View style={hostStyle} testID='message-overlay-message'>
|
|
401
|
+
<PortalHost name='message-overlay' style={styles.absoluteFill} />
|
|
402
|
+
</Animated.View>
|
|
403
|
+
</GestureDetector>
|
|
404
|
+
|
|
317
405
|
<Animated.View style={topItemStyle} testID='message-overlay-top'>
|
|
318
406
|
<PortalHost name='top-item' style={styles.absoluteFill} />
|
|
319
407
|
</Animated.View>
|
|
320
408
|
|
|
321
|
-
<Animated.View
|
|
322
|
-
pointerEvents='box-none'
|
|
323
|
-
style={hostStyle}
|
|
324
|
-
testID='message-overlay-message'
|
|
325
|
-
>
|
|
326
|
-
<PortalHost name='message-overlay' style={styles.absoluteFill} />
|
|
327
|
-
</Animated.View>
|
|
328
|
-
|
|
329
409
|
<Animated.View style={bottomItemStyle} testID='message-overlay-bottom'>
|
|
330
410
|
<PortalHost name='bottom-item' style={styles.absoluteFill} />
|
|
331
411
|
</Animated.View>
|