This commit is contained in:
Tomek Zawadzki
2026-07-29 19:57:21 +02:00
committed by Oleksii Bulenok
parent 3d39ca0c0c
commit 95ce988fb7
15 changed files with 279 additions and 313 deletions
+1 -1
View File
@@ -234,7 +234,7 @@
"react-native-device-attest": "^0.1.6", "react-native-device-attest": "^0.1.6",
"react-native-drawer-layout": "^4.2.3", "react-native-drawer-layout": "^4.2.3",
"react-native-edge-to-edge": "^1.8.1", "react-native-edge-to-edge": "^1.8.1",
"react-native-gesture-handler": "~2.32.0", "react-native-gesture-handler": "~3.1.0",
"react-native-keyboard-controller": "1.21.9", "react-native-keyboard-controller": "1.21.9",
"react-native-mmkv": "^3.3.3", "react-native-mmkv": "^3.3.3",
"react-native-pager-view": "6.8.0", "react-native-pager-view": "6.8.0",
@@ -36,3 +36,12 @@ index 38470a658878a63919186257041098747ec55473..b9d25798c9be229efeb676166f89bfbc
if (open) { if (open) {
runOnJS(onOpen)(); runOnJS(onOpen)();
} else { } else {
diff --git a/lib/typescript/src/types.d.ts b/lib/typescript/src/types.d.ts
--- a/lib/typescript/src/types.d.ts
+++ b/lib/typescript/src/types.d.ts
@@ -1,4 +1,4 @@
import * as React from 'react';
import type { StyleProp, View, ViewStyle } from 'react-native';
-import type { PanGesture } from 'react-native-gesture-handler';
+import type { LegacyPanGesture as PanGesture } from 'react-native-gesture-handler';
import type { SharedValue } from 'react-native-reanimated';
+13 -42
View File
@@ -228,7 +228,7 @@ patchedDependencies:
expo@57.0.8: 1722861d12907a2d412d04230545b2fcbfa5547a79f99d763cb3e8de6f3a6f2a expo@57.0.8: 1722861d12907a2d412d04230545b2fcbfa5547a79f99d763cb3e8de6f3a6f2a
react-native-date-picker@5.0.13: 92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd react-native-date-picker@5.0.13: 92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd
react-native-dotenv@3.4.11: 16b34eb974399935d3cf7c2526534f906991ccd876215176658347059cdd2021 react-native-dotenv@3.4.11: 16b34eb974399935d3cf7c2526534f906991ccd876215176658347059cdd2021
react-native-drawer-layout@4.2.3: 74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130 react-native-drawer-layout@4.2.3: 2a67e397c87df9fb947b1931c9b00724b802a8dca5d3013617e6155f1ae3188e
react-native-keyboard-controller@1.21.9: ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271 react-native-keyboard-controller@1.21.9: ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271
react-native-pager-view@6.8.0: c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc react-native-pager-view@6.8.0: c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc
react-native-screens@4.26.2: ba3295d44434a729f143a6b2c24e4a9b2f911ab200b2bc47e46cdfeaa1536f7c react-native-screens@4.26.2: ba3295d44434a729f143a6b2c24e4a9b2f911ab200b2bc47e46cdfeaa1536f7c
@@ -640,13 +640,13 @@ importers:
version: 0.1.6(expo@57.0.8)(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) version: 0.1.6(expo@57.0.8)(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-drawer-layout: react-native-drawer-layout:
specifier: ^4.2.3 specifier: ^4.2.3
version: 4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(react-native-gesture-handler@2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) version: 4.2.3(patch_hash=2a67e397c87df9fb947b1931c9b00724b802a8dca5d3013617e6155f1ae3188e)(react-native-gesture-handler@3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-edge-to-edge: react-native-edge-to-edge:
specifier: ^1.8.1 specifier: ^1.8.1
version: 1.8.1(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) version: 1.8.1(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-gesture-handler: react-native-gesture-handler:
specifier: ~2.32.0 specifier: ~3.1.0
version: 2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) version: 3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-keyboard-controller: react-native-keyboard-controller:
specifier: 1.21.9 specifier: 1.21.9
version: 1.21.9(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) version: 1.21.9(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
@@ -715,7 +715,7 @@ importers:
version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3) version: 2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
sonner-native: sonner-native:
specifier: 0.26.4 specifier: 0.26.4
version: 0.26.4(a1115538eb9b7f01f00730be61406201) version: 0.26.4(58a7fe7e7df4354448cb52f8cc2a8c5c)
tippy.js: tippy.js:
specifier: ^6.3.7 specifier: ^6.3.7
version: 6.3.7 version: 6.3.7
@@ -1764,10 +1764,6 @@ packages:
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
'@egjs/hammerjs@2.0.17':
resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==}
engines: {node: '>=0.8.0'}
'@emnapi/runtime@1.11.3': '@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
@@ -3990,9 +3986,6 @@ packages:
'@types/graceful-fs@4.1.9': '@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
'@types/hammerjs@2.0.46':
resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
'@types/html-minifier-terser@6.1.0': '@types/html-minifier-terser@6.1.0':
resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==}
@@ -4080,9 +4073,6 @@ packages:
'@types/react-test-renderer@19.1.0': '@types/react-test-renderer@19.1.0':
resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==} resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==}
'@types/react@19.1.17':
resolution: {integrity: sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==}
'@types/react@19.2.18': '@types/react@19.2.18':
resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==}
@@ -6312,9 +6302,6 @@ packages:
hls.js@1.6.16: hls.js@1.6.16:
resolution: {integrity: sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==} resolution: {integrity: sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==}
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
hosted-git-info@7.0.2: hosted-git-info@7.0.2:
resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==}
engines: {node: ^16.14.0 || >=18.0.0} engines: {node: ^16.14.0 || >=18.0.0}
@@ -8369,8 +8356,8 @@ packages:
react: '*' react: '*'
react-native: '*' react-native: '*'
react-native-gesture-handler@2.32.0: react-native-gesture-handler@3.1.0:
resolution: {integrity: sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA==} resolution: {integrity: sha512-+kWVyZ6vLdtyFa1/aOc/sZncLAVPUKxhji/ttbiLI7hOaSU44bLjhI7p+Ns+pMl2r3RqPXssl5qHReYet9G77g==}
peerDependencies: peerDependencies:
react: '*' react: '*'
react-native: '*' react-native: '*'
@@ -10964,10 +10951,6 @@ snapshots:
'@discoveryjs/json-ext@0.5.7': {} '@discoveryjs/json-ext@0.5.7': {}
'@egjs/hammerjs@2.0.17':
dependencies:
'@types/hammerjs': 2.0.46
'@emnapi/runtime@1.11.3': '@emnapi/runtime@1.11.3':
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@@ -13580,8 +13563,6 @@ snapshots:
dependencies: dependencies:
'@types/node': 24.12.4 '@types/node': 24.12.4
'@types/hammerjs@2.0.46': {}
'@types/html-minifier-terser@6.1.0': {} '@types/html-minifier-terser@6.1.0': {}
'@types/http-errors@2.0.5': {} '@types/http-errors@2.0.5': {}
@@ -13673,11 +13654,7 @@ snapshots:
'@types/react-test-renderer@19.1.0': '@types/react-test-renderer@19.1.0':
dependencies: dependencies:
'@types/react': 19.1.17 '@types/react': 19.2.18
'@types/react@19.1.17':
dependencies:
csstype: 3.2.3
'@types/react@19.2.18': '@types/react@19.2.18':
dependencies: dependencies:
@@ -16164,10 +16141,6 @@ snapshots:
hls.js@1.6.16: {} hls.js@1.6.16: {}
hoist-non-react-statics@3.3.2:
dependencies:
react-is: 16.13.1
hosted-git-info@7.0.2: hosted-git-info@7.0.2:
dependencies: dependencies:
lru-cache: 10.4.3 lru-cache: 10.4.3
@@ -18481,12 +18454,12 @@ snapshots:
'@babel/runtime': 7.29.2 '@babel/runtime': 7.29.2
dotenv: 16.6.1 dotenv: 16.6.1
? react-native-drawer-layout@4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(react-native-gesture-handler@2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) ? react-native-drawer-layout@4.2.3(patch_hash=2a67e397c87df9fb947b1931c9b00724b802a8dca5d3013617e6155f1ae3188e)(react-native-gesture-handler@3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3))(react-native-reanimated@4.6.0(92d1a74bc123f334ae9968fa1c80814f))(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
: dependencies: : dependencies:
color: 4.2.3 color: 4.2.3
react: 19.2.3 react: 19.2.3
react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)
react-native-gesture-handler: 2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-gesture-handler: 3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-reanimated: 4.6.0(92d1a74bc123f334ae9968fa1c80814f) react-native-reanimated: 4.6.0(92d1a74bc123f334ae9968fa1c80814f)
use-latest-callback: 0.2.6(react@19.2.3) use-latest-callback: 0.2.6(react@19.2.3)
@@ -18495,11 +18468,9 @@ snapshots:
react: 19.2.3 react: 19.2.3
react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)
react-native-gesture-handler@2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): react-native-gesture-handler@3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3):
dependencies: dependencies:
'@egjs/hammerjs': 2.0.17
'@types/react-test-renderer': 19.1.0 '@types/react-test-renderer': 19.1.0
hoist-non-react-statics: 3.3.2
invariant: 2.2.4 invariant: 2.2.4
react: 19.2.3 react: 19.2.3
react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)
@@ -19164,11 +19135,11 @@ snapshots:
dependencies: dependencies:
atomic-sleep: 1.0.0 atomic-sleep: 1.0.0
sonner-native@0.26.4(a1115538eb9b7f01f00730be61406201): sonner-native@0.26.4(58a7fe7e7df4354448cb52f8cc2a8c5c):
dependencies: dependencies:
react: 19.2.3 react: 19.2.3
react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) react-native: 0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)
react-native-gesture-handler: 2.32.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-gesture-handler: 3.1.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-reanimated: 4.6.0(92d1a74bc123f334ae9968fa1c80814f) react-native-reanimated: 4.6.0(92d1a74bc123f334ae9968fa1c80814f)
react-native-safe-area-context: 5.7.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-safe-area-context: 5.7.0(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
react-native-screens: 4.26.2(patch_hash=ba3295d44434a729f143a6b2c24e4a9b2f911ab200b2bc47e46cdfeaa1536f7c)(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-screens: 4.26.2(patch_hash=ba3295d44434a729f143a6b2c24e4a9b2f911ab200b2bc47e46cdfeaa1536f7c)(react-native@0.86.0(patch_hash=239208992c380d6785576d1fae2e3f60f60f509d7b09d56bf552be29d6c42a06)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/jest-preset@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)
+49 -54
View File
@@ -20,11 +20,12 @@ import {
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type GestureStateChangeEvent, type PanGestureActiveEvent,
type GestureUpdateEvent, useCompetingGestures,
type PanGestureHandlerEventPayload, useExclusiveGestures,
usePanGesture,
useTapGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import {KeyboardEvents} from 'react-native-keyboard-controller' import {KeyboardEvents} from 'react-native-keyboard-controller'
import Animated, { import Animated, {
@@ -299,24 +300,19 @@ export function Trigger({
} }
}, [context, insets]) }, [context, insets])
const tapGesture = useMemo(() => { const tapGesture = useTapGesture({
const gesture = Gesture.Tap() numberOfTaps: 1,
.numberOfTaps(1) cancelsTouchesInView: false,
.cancelsTouchesInView(false) runOnJS: true,
.runOnJS(true) onDeactivate: onTap ? () => void onTap() : undefined,
if (onTap) { })
gesture.onEnd(() => void onTap())
}
return gesture
}, [onTap])
const doubleTapGesture = useMemo(() => { const doubleTapGesture = useTapGesture({
return Gesture.Tap() numberOfTaps: 2,
.numberOfTaps(2) hitSlop: HITSLOP_10,
.hitSlop(HITSLOP_10) runOnJS: true,
.onEnd(() => void open('auxiliary-only')) onDeactivate: () => void open('auxiliary-only'),
.runOnJS(true) })
}, [open])
const { const {
hoverablesSV, hoverablesSV,
@@ -336,34 +332,33 @@ export function Trigger({
}, },
) )
const pressAndHoldGesture = useMemo(() => { const pressAndHoldGesture = usePanGesture({
return Gesture.Pan() activateAfterLongPress: 500,
.activateAfterLongPress(500) cancelsTouchesInView: false,
.cancelsTouchesInView(false) averageTouches: true,
.averageTouches(true) onActivate: () => {
.onStart(() => { 'worklet'
'worklet' scheduleOnRN(open, 'full')
scheduleOnRN(open, 'full') },
}) onUpdate: evt => {
.onUpdate(evt => { 'worklet'
'worklet' const item = getHoveredHoverable(evt, hoverablesSV, translationSV)
const item = getHoveredHoverable(evt, hoverablesSV, translationSV) hoveredItemSV.set(item)
hoveredItemSV.set(item) },
}) onDeactivate: () => {
.onEnd(() => { 'worklet'
'worklet' // don't recalculate hovered item - if they haven't moved their finger from
// don't recalculate hovered item - if they haven't moved their finger from // the initial press, it's jarring to then select the item underneath
// the initial press, it's jarring to then select the item underneath // as the menu may have slid into place beneath their finger
// as the menu may have slid into place beneath their finger const item = hoveredItemSV.get()
const item = hoveredItemSV.get() if (item) {
if (item) { scheduleOnRN(onTouchUpMenuItem, item)
scheduleOnRN(onTouchUpMenuItem, item) }
} },
}) })
}, [open, hoverablesSV, onTouchUpMenuItem, hoveredItemSV, translationSV])
// Order matters here: doubleTapGesture must come before tapGesture. // Order matters here: doubleTapGesture must come before tapGesture.
const tapAndHoldGestures = Gesture.Exclusive( const tapAndHoldGestures = useExclusiveGestures(
doubleTapGesture, doubleTapGesture,
tapGesture, tapGesture,
pressAndHoldGesture, pressAndHoldGesture,
@@ -371,11 +366,13 @@ export function Trigger({
// An optional swipe gesture (e.g. swipe-to-reply) races against the tap/hold // An optional swipe gesture (e.g. swipe-to-reply) races against the tap/hold
// group: whichever activates first wins and cancels the rest, so they're // group: whichever activates first wins and cancels the rest, so they're
// mutually exclusive. Race (not Exclusive) avoids a held-but-not-yet-moved // mutually exclusive. Competing (not Exclusive) avoids a held-but-not-yet-moved
// swipe Pan blocking the long-press from firing. // swipe Pan blocking the long-press from firing.
const composedGestures = swipeGesture const composedGestures = useCompetingGestures(
? Gesture.Race(swipeGesture, tapAndHoldGestures) ...(swipeGesture
: tapAndHoldGestures ? [swipeGesture, tapAndHoldGestures]
: [tapAndHoldGestures]),
)
const measurement = context.measurement || pendingMeasurement?.measurement const measurement = context.measurement || pendingMeasurement?.measurement
@@ -990,9 +987,7 @@ function measureView(
} }
function getHoveredHoverable( function getHoveredHoverable(
evt: evt: PanGestureActiveEvent,
| GestureStateChangeEvent<PanGestureHandlerEventPayload>
| GestureUpdateEvent<PanGestureHandlerEventPayload>,
hoverables: SharedValue<Record<string, {id: string; rect: Measurement}>>, hoverables: SharedValue<Record<string, {id: string; rect: Measurement}>>,
translation: SharedValue<number>, translation: SharedValue<number>,
) { ) {
+6 -5
View File
@@ -4,7 +4,7 @@ import {
type StyleProp, type StyleProp,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type GestureType} from 'react-native-gesture-handler' import {type PanGesture} from 'react-native-gesture-handler'
import {type SharedValue} from 'react-native-reanimated' import {type SharedValue} from 'react-native-reanimated'
import type * as Dialog from '#/components/Dialog' import type * as Dialog from '#/components/Dialog'
@@ -89,7 +89,7 @@ export type TriggerProps = {
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
/** /**
* Callback for single taps. Composed with the double-tap and * Callback for single taps. Composed with the double-tap and
* press-and-hold gestures via `Gesture.Exclusive`, so a double tap * press-and-hold gestures via `useExclusiveGestures`, so a double tap
* does not also fire this handler. * does not also fire this handler.
* *
* @platform ios, android * @platform ios, android
@@ -97,12 +97,13 @@ export type TriggerProps = {
onTap?: () => void onTap?: () => void
/** /**
* An optional gesture (e.g. swipe-to-reply) composed into the same * An optional gesture (e.g. swipe-to-reply) composed into the same
* arbitration group as the tap and press-and-hold gestures via `Gesture.Race`, * arbitration group as the tap and press-and-hold gestures via
* making it mutually exclusive with them - only one can win a given touch. * `useCompetingGestures`, making it mutually exclusive with them - only one
* can win a given touch.
* *
* @platform ios, android * @platform ios, android
*/ */
swipeGesture?: GestureType swipeGesture?: PanGesture
} }
export type TriggerChildProps = export type TriggerChildProps =
| { | {
+12 -11
View File
@@ -1,5 +1,5 @@
import {useLayoutEffect, useRef} from 'react' import {useLayoutEffect, useRef} from 'react'
import {Gesture, GestureDetector} from 'react-native-gesture-handler' import {GestureDetector, usePanGesture} from 'react-native-gesture-handler'
import Animated, { import Animated, {
type AnimatedRef, type AnimatedRef,
measure, measure,
@@ -253,8 +253,8 @@ function SortableItem<T>({
const lastHapticSlot = useSharedValue(-1) const lastHapticSlot = useSharedValue(-1)
const gesture = Gesture.Pan() const gesture = usePanGesture({
.onStart(() => { onActivate: () => {
'worklet' 'worklet'
const s = state.get() const s = state.get()
const mySlot = s.slots[itemKey] const mySlot = s.slots[itemKey]
@@ -268,8 +268,8 @@ function SortableItem<T>({
scheduleOnRN(onDragStart) scheduleOnRN(onDragStart)
} }
scheduleOnRN(playHaptic) scheduleOnRN(playHaptic)
}) },
.onChange(e => { onUpdate: e => {
'worklet' 'worklet'
const startSlot = state.get().dragStartSlot const startSlot = state.get().dragStartSlot
const minY = -startSlot * itemHeight const minY = -startSlot * itemHeight
@@ -287,8 +287,8 @@ function SortableItem<T>({
lastHapticSlot.set(clampedSlot) lastHapticSlot.set(clampedSlot)
scheduleOnRN(playHaptic, 'Light') scheduleOnRN(playHaptic, 'Light')
} }
}) },
.onEnd(() => { onDeactivate: () => {
'worklet' 'worklet'
// Stop auto-scroll BEFORE the snap animation. // Stop auto-scroll BEFORE the snap animation.
isGestureActive.set(false) isGestureActive.set(false)
@@ -338,9 +338,9 @@ function SortableItem<T>({
} }
}), }),
) )
}) },
// Reset if the gesture is cancelled without onEnd firing. // Reset if the gesture is cancelled without onDeactivate firing.
.onFinalize(() => { onFinalize: () => {
'worklet' 'worklet'
isGestureActive.set(false) isGestureActive.set(false)
if (state.get().activeKey === itemKey && dragY.get() === 0) { if (state.get().activeKey === itemKey && dragY.get() === 0) {
@@ -350,7 +350,8 @@ function SortableItem<T>({
scheduleOnRN(onDragEnd) scheduleOnRN(onDragEnd)
} }
} }
}) },
})
// All vertical positioning is via translateY (no `top`). This avoids // All vertical positioning is via translateY (no `top`). This avoids
// discrete jumps when slots change — Reanimated smoothly animates from // discrete jumps when slots change — Reanimated smoothly animates from
@@ -1,9 +1,14 @@
import {memo, useState} from 'react' import {memo, useState} from 'react'
import {ActivityIndicator, StyleSheet} from 'react-native' import {ActivityIndicator, StyleSheet} from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type PanGesture, type PanGesture,
useExclusiveGestures,
useManualGesture,
usePanGesture,
usePinchGesture,
useSimultaneousGestures,
useTapGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import Animated, { import Animated, {
type AnimatableValue, type AnimatableValue,
@@ -137,16 +142,16 @@ const ImageItem = ({
return [dx, dy] return [dx, dy]
} }
const pinch = Gesture.Pinch() const pinch = usePinchGesture({
.onStart(e => { onActivate: e => {
'worklet' 'worklet'
const screenSize = measureSafeArea() const screenSize = measureSafeArea()
pinchOrigin.set({ pinchOrigin.set({
x: e.focalX - screenSize.width / 2, x: e.focalX - screenSize.width / 2,
y: e.focalY - screenSize.height / 2, y: e.focalY - screenSize.height / 2,
}) })
}) },
.onChange(e => { onUpdate: e => {
'worklet' 'worklet'
const screenSize = measureSafeArea() const screenSize = measureSafeArea()
if (!imageDimensions) { if (!imageDimensions) {
@@ -181,8 +186,8 @@ const ImageItem = ({
y: pt.y + dy, y: pt.y + dy,
}) })
} }
}) },
.onEnd(() => { onDeactivate: () => {
'worklet' 'worklet'
// Commit just the pinch. // Commit just the pinch.
let t = createTransform() let t = createTransform()
@@ -200,13 +205,14 @@ const ImageItem = ({
pinchScale.set(1) pinchScale.set(1)
pinchOrigin.set({x: 0, y: 0}) pinchOrigin.set({x: 0, y: 0})
pinchTranslation.set({x: 0, y: 0}) pinchTranslation.set({x: 0, y: 0})
}) },
})
const pan = Gesture.Pan() const pan = usePanGesture({
.averageTouches(true) averageTouches: true,
// Unlike .enabled(isScaled), this ensures that an initial pinch can turn into a pan midway: // Unlike enabled: isScaled, this ensures that an initial pinch can turn into a pan midway:
.minPointers(isScaled ? 1 : 2) minPointers: isScaled ? 1 : 2,
.onChange(e => { onUpdate: e => {
'worklet' 'worklet'
const screenSize = measureSafeArea() const screenSize = measureSafeArea()
if (!imageDimensions) { if (!imageDimensions) {
@@ -229,8 +235,8 @@ const ImageItem = ({
nextPanTranslation.x += dx nextPanTranslation.x += dx
nextPanTranslation.y += dy nextPanTranslation.y += dy
panTranslation.set(nextPanTranslation) panTranslation.set(nextPanTranslation)
}) },
.onEnd(() => { onDeactivate: () => {
'worklet' 'worklet'
// Commit just the pan. // Commit just the pan.
let t = createTransform() let t = createTransform()
@@ -241,16 +247,19 @@ const ImageItem = ({
// Reset just the pan. // Reset just the pan.
panTranslation.set({x: 0, y: 0}) panTranslation.set({x: 0, y: 0})
}) },
const singleTap = Gesture.Tap().onEnd(() => {
'worklet'
scheduleOnRN(onTap)
}) })
const doubleTap = Gesture.Tap() const singleTap = useTapGesture({
.numberOfTaps(2) onDeactivate: () => {
.onEnd(e => { 'worklet'
scheduleOnRN(onTap)
},
})
const doubleTap = useTapGesture({
numberOfTaps: 2,
onDeactivate: e => {
'worklet' 'worklet'
const screenSize = measureSafeArea() const screenSize = measureSafeArea()
if (!imageDimensions || !imageAspect) { if (!imageDimensions || !imageAspect) {
@@ -295,17 +304,21 @@ const ImageItem = ({
const finalTransform = createTransform() const finalTransform = createTransform()
prependPinch(finalTransform, scale, origin, {x: dx, y: dy}) prependPinch(finalTransform, scale, origin, {x: dx, y: dy})
committedTransform.set(withClampedSpring(finalTransform)) committedTransform.set(withClampedSpring(finalTransform))
}) },
})
// If the parent is not at rest, provide a no-op gesture.
const noopGesture = useManualGesture()
const pinchAndPan = useSimultaneousGestures(pinch, pan)
const exclusiveGesture = useExclusiveGestures(
dismissSwipePan,
pinchAndPan,
doubleTap,
singleTap,
)
const composedGesture = isScrollViewBeingDragged const composedGesture = isScrollViewBeingDragged
? // If the parent is not at rest, provide a no-op gesture. ? noopGesture
Gesture.Manual() : exclusiveGesture
: Gesture.Exclusive(
dismissSwipePan,
Gesture.Simultaneous(pinch, pan),
doubleTap,
singleTap,
)
const containerStyle = useAnimatedStyle(() => { const containerStyle = useAnimatedStyle(() => {
const {scaleAndMoveTransform, isHidden} = transforms.get() const {scaleAndMoveTransform, isHidden} = transforms.get()
@@ -9,9 +9,10 @@
import {memo, useState} from 'react' import {memo, useState} from 'react'
import {ActivityIndicator, StyleSheet} from 'react-native' import {ActivityIndicator, StyleSheet} from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type PanGesture, type PanGesture,
useExclusiveGestures,
useTapGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import Animated, { import Animated, {
type SharedValue, type SharedValue,
@@ -115,14 +116,16 @@ const ImageItem = ({
}) })
} }
const singleTap = Gesture.Tap().onEnd(() => { const singleTap = useTapGesture({
'worklet' onDeactivate: () => {
scheduleOnRN(onTap) 'worklet'
scheduleOnRN(onTap)
},
}) })
const doubleTap = Gesture.Tap() const doubleTap = useTapGesture({
.numberOfTaps(2) numberOfTaps: 2,
.onEnd(e => { onDeactivate: e => {
'worklet' 'worklet'
const screenSize = measureSafeArea() const screenSize = measureSafeArea()
const {absoluteX, absoluteY} = e const {absoluteX, absoluteY} = e
@@ -142,9 +145,10 @@ const ImageItem = ({
) )
} }
scheduleOnRN(zoomTo, nextZoomRect) scheduleOnRN(zoomTo, nextZoomRect)
}) },
})
const composedGesture = Gesture.Exclusive( const composedGesture = useExclusiveGestures(
dismissSwipePan, dismissSwipePan,
doubleTap, doubleTap,
singleTap, singleTap,
+11 -10
View File
@@ -10,7 +10,7 @@
import {useCallback, useEffect, useMemo, useState} from 'react' import {useCallback, useEffect, useMemo, useState} from 'react'
import {PixelRatio, StyleSheet, useWindowDimensions, View} from 'react-native' import {PixelRatio, StyleSheet, useWindowDimensions, View} from 'react-native'
import {SystemBars} from 'react-native-edge-to-edge' import {SystemBars} from 'react-native-edge-to-edge'
import {Gesture} from 'react-native-gesture-handler' import {usePanGesture} from 'react-native-gesture-handler'
import PagerView, { import PagerView, {
type PagerViewOnPageSelectedEvent, type PagerViewOnPageSelectedEvent,
type PageScrollStateChangedNativeEvent, type PageScrollStateChangedNativeEvent,
@@ -566,19 +566,19 @@ function LightboxImage({
} }
}) })
const dismissSwipePan = Gesture.Pan() const dismissSwipePan = usePanGesture({
.enabled(isActive && !isScaled) enabled: isActive && !isScaled,
.activeOffsetY([-10, 10]) activeOffsetY: [-10, 10],
.failOffsetX([-10, 10]) failOffsetX: [-10, 10],
.maxPointers(1) maxPointers: 1,
.onUpdate(e => { onUpdate: e => {
'worklet' 'worklet'
if (openProgress.get() !== 1 || isFlyingAway.get()) { if (openProgress.get() !== 1 || isFlyingAway.get()) {
return return
} }
dismissSwipeTranslateY.set(e.translationY) dismissSwipeTranslateY.set(e.translationY)
}) },
.onEnd(e => { onDeactivate: e => {
'worklet' 'worklet'
if (openProgress.get() !== 1 || isFlyingAway.get()) { if (openProgress.get() !== 1 || isFlyingAway.get()) {
return return
@@ -608,7 +608,8 @@ function LightboxImage({
}), }),
) )
} }
}) },
})
return ( return (
<ImageItem <ImageItem
+2 -2
View File
@@ -1,6 +1,6 @@
import {memo, useCallback} from 'react' import {memo, useCallback} from 'react'
import {Platform} from 'react-native' import {Platform} from 'react-native'
import {type GestureType} from 'react-native-gesture-handler' import {type PanGesture} from 'react-native-gesture-handler'
import * as Clipboard from 'expo-clipboard' import * as Clipboard from 'expo-clipboard'
import {type ModerationOpts} from '@bsky/sdk/moderation' import {type ModerationOpts} from '@bsky/sdk/moderation'
import {RichText} from '@bsky/sdk/richtext' import {RichText} from '@bsky/sdk/richtext'
@@ -47,7 +47,7 @@ export let MessageContextMenu = ({
* Native only. A swipe gesture (swipe-to-reply) composed into the trigger's * Native only. A swipe gesture (swipe-to-reply) composed into the trigger's
* gesture group so it's mutually exclusive with the tap and long-press. * gesture group so it's mutually exclusive with the tap and long-press.
*/ */
swipeGesture?: GestureType swipeGesture?: PanGesture
}): React.ReactNode => { }): React.ReactNode => {
const {t: l, i18n} = useLingui() const {t: l, i18n} = useLingui()
const ax = useAnalytics() const ax = useAnalytics()
+53 -70
View File
@@ -1,6 +1,5 @@
import {useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {Gesture, type GestureType} from 'react-native-gesture-handler' import {type PanGesture, usePanGesture} from 'react-native-gesture-handler'
import Animated, { import Animated, {
clamp, clamp,
interpolate, interpolate,
@@ -52,8 +51,9 @@ function applyResistance(value: number) {
* *
* The pan gesture is built here but handed to `children` via a render prop so * The pan gesture is built here but handed to `children` via a render prop so
* the consumer can compose it into the message's context-menu gestures with * the consumer can compose it into the message's context-menu gestures with
* `Gesture.Exclusive`. Sharing one arbitration group is what makes the swipe * `useCompetingGestures`. Sharing one arbitration group is what makes the
* and the long-press mutually exclusive - only one can ever win the touch. * swipe and the long-press mutually exclusive - only one can ever win the
* touch.
*/ */
export function SwipeToReply({ export function SwipeToReply({
isFromSelf, isFromSelf,
@@ -64,7 +64,7 @@ export function SwipeToReply({
isFromSelf: boolean isFromSelf: boolean
onReply: () => void onReply: () => void
enabled?: boolean enabled?: boolean
children: (swipeGesture: GestureType) => React.ReactNode children: (swipeGesture: PanGesture) => React.ReactNode
}) { }) {
const t = useTheme() const t = useTheme()
const playHaptic = useHaptics() const playHaptic = useHaptics()
@@ -74,73 +74,56 @@ export function SwipeToReply({
const hit = useSharedValue(false) const hit = useSharedValue(false)
const iconScale = useSharedValue(1) const iconScale = useSharedValue(1)
const swipeGesture = useMemo(() => { const runPop = () => {
const runPop = () => { 'worklet'
'worklet' if (isReducedMotion) return
if (isReducedMotion) return iconScale.set(
iconScale.set( withSequence(
withSequence( withTiming(1.2, {duration: 175}),
withTiming(1.2, {duration: 175}), withTiming(1, {duration: 100}),
withTiming(1, {duration: 100}), ),
),
)
}
return (
Gesture.Pan()
.enabled(enabled)
// Arm only on the inward axis; the outward direction is effectively
// disabled so the bubble can't be dragged off its own edge.
.activeOffsetX(
isFromSelf
? [-10, EFFECTIVELY_DISABLED_OFFSET]
: [-EFFECTIVELY_DISABLED_OFFSET, 10],
)
.activeOffsetY([
-EFFECTIVELY_DISABLED_OFFSET,
EFFECTIVELY_DISABLED_OFFSET,
])
.onChange(e => {
'worklet'
const dir = isFromSelf
? Math.min(e.translationX, 0)
: Math.max(e.translationX, 0)
transX.set(applyResistance(dir))
const pastThreshold = Math.abs(transX.get()) >= ACTIVATION_THRESHOLD
if (pastThreshold && !hit.get()) {
hit.set(true)
runPop()
scheduleOnRN(playHaptic, 'Medium')
} else if (!pastThreshold && hit.get()) {
hit.set(false)
}
})
.onEnd(() => {
'worklet'
// Only a clean end (finger lifted past threshold) triggers the reply.
if (hit.get()) {
scheduleOnRN(onReply)
}
})
.onFinalize(() => {
'worklet'
// Runs on both end and cancellation, so the bubble always animates
// home even if the gesture is interrupted mid-swipe.
transX.set(withTiming(0, {duration: 200}))
hit.set(false)
})
) )
}, [ }
isFromSelf,
const swipeGesture = usePanGesture({
enabled, enabled,
onReply, // Arm only on the inward axis; the outward direction is effectively
playHaptic, // disabled so the bubble can't be dragged off its own edge.
isReducedMotion, activeOffsetX: isFromSelf
transX, ? [-10, EFFECTIVELY_DISABLED_OFFSET]
hit, : [-EFFECTIVELY_DISABLED_OFFSET, 10],
iconScale, activeOffsetY: [-EFFECTIVELY_DISABLED_OFFSET, EFFECTIVELY_DISABLED_OFFSET],
]) onUpdate: e => {
'worklet'
const dir = isFromSelf
? Math.min(e.translationX, 0)
: Math.max(e.translationX, 0)
transX.set(applyResistance(dir))
const pastThreshold = Math.abs(transX.get()) >= ACTIVATION_THRESHOLD
if (pastThreshold && !hit.get()) {
hit.set(true)
runPop()
scheduleOnRN(playHaptic, 'Medium')
} else if (!pastThreshold && hit.get()) {
hit.set(false)
}
},
onDeactivate: () => {
'worklet'
// Only a clean end (finger lifted past threshold) triggers the reply.
if (hit.get()) {
scheduleOnRN(onReply)
}
},
onFinalize: () => {
'worklet'
// Runs on both end and cancellation, so the bubble always animates
// home even if the gesture is interrupted mid-swipe.
transX.set(withTiming(0, {duration: 200}))
hit.set(false)
},
})
const contentStyle = useAnimatedStyle(() => ({ const contentStyle = useAnimatedStyle(() => ({
transform: [{translateX: transX.get()}], transform: [{translateX: transX.get()}],
+13 -14
View File
@@ -1,6 +1,6 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {type ColorValue, Dimensions, View} from 'react-native' import {type ColorValue, Dimensions, View} from 'react-native'
import {Gesture, GestureDetector} from 'react-native-gesture-handler' import {GestureDetector, usePanGesture} from 'react-native-gesture-handler'
import Animated, { import Animated, {
clamp, clamp,
interpolate, interpolate,
@@ -106,17 +106,17 @@ export function GestureActionView({
// Absurdly high value so it doesn't interfere with the pan gestures above (i.e., scroll) // Absurdly high value so it doesn't interfere with the pan gestures above (i.e., scroll)
// reanimated doesn't offer great support for disabling y/x axes :/ // reanimated doesn't offer great support for disabling y/x axes :/
const effectivelyDisabledOffset = 200 const effectivelyDisabledOffset = 200
const panGesture = Gesture.Pan() const panGesture = usePanGesture({
.activeOffsetX([ activeOffsetX: [
actions.leftFirst ? -10 : -effectivelyDisabledOffset, actions.leftFirst ? -10 : -effectivelyDisabledOffset,
actions.rightFirst ? 10 : effectivelyDisabledOffset, actions.rightFirst ? 10 : effectivelyDisabledOffset,
]) ],
.activeOffsetY([-effectivelyDisabledOffset, effectivelyDisabledOffset]) activeOffsetY: [-effectivelyDisabledOffset, effectivelyDisabledOffset],
.onStart(() => { onActivate: () => {
'worklet' 'worklet'
isActive.set(true) isActive.set(true)
}) },
.onChange(e => { onUpdate: e => {
'worklet' 'worklet'
transX.set(e.translationX) transX.set(e.translationX)
@@ -189,8 +189,8 @@ export function GestureActionView({
} }
} }
} }
}) },
.onEnd(e => { onDeactivate: e => {
'worklet' 'worklet'
if (e.translationX < 0) { if (e.translationX < 0) {
if (hitSecond.get() && actions.leftSecond) { if (hitSecond.get() && actions.leftSecond) {
@@ -209,9 +209,8 @@ export function GestureActionView({
hitFirst.set(false) hitFirst.set(false)
hitSecond.set(false) hitSecond.set(false)
isActive.set(false) isActive.set(false)
}) },
})
const composedGesture = Gesture.Simultaneous(panGesture)
const animatedSliderStyle = useAnimatedStyle(() => { const animatedSliderStyle = useAnimatedStyle(() => {
return { return {
@@ -286,7 +285,7 @@ export function GestureActionView({
}) })
return ( return (
<GestureDetector gesture={composedGesture}> <GestureDetector gesture={panGesture}>
<View> <View>
<Animated.View style={[a.absolute, a.inset_0, animatedBackgroundStyle]}> <Animated.View style={[a.absolute, a.inset_0, animatedBackgroundStyle]}>
<View <View
+31 -41
View File
@@ -1,9 +1,9 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type NativeGesture, type NativeGesture,
usePanGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import Animated, { import Animated, {
clamp, clamp,
@@ -85,48 +85,38 @@ export function Scrubber({
[player, isSeekingSV, seekingAnimationSV], [player, isSeekingSV, seekingAnimationSV],
) )
const scrubPanGesture = useMemo(() => { const scrubPanGesture = usePanGesture({
return Gesture.Pan() block: scrollGesture,
.blocksExternalGesture(scrollGesture) activeOffsetX: [-10, 10],
.activeOffsetX([-10, 10]) failOffsetY: [-10, 10],
.failOffsetY([-10, 10]) onActivate: () => {
.onStart(() => { 'worklet'
'worklet' seekProgressSV.set(currentTimeSV.get())
seekProgressSV.set(currentTimeSV.get()) isSeekingSV.set(true)
isSeekingSV.set(true) seekingAnimationSV.set(withTiming(1, {duration: 500}))
seekingAnimationSV.set(withTiming(1, {duration: 500})) },
}) onUpdate: evt => {
.onUpdate(evt => { 'worklet'
'worklet' const progress = evt.x / screenWidth
const progress = evt.x / screenWidth seekProgressSV.set(
seekProgressSV.set( clamp(progress * durationSV.get(), 0, durationSV.get()),
clamp(progress * durationSV.get(), 0, durationSV.get()), )
) },
}) onDeactivate: evt => {
.onEnd(evt => { 'worklet'
'worklet' isSeekingSV.get()
isSeekingSV.get()
const progress = evt.x / screenWidth const progress = evt.x / screenWidth
const newTime = clamp(progress * durationSV.get(), 0, durationSV.get()) const newTime = clamp(progress * durationSV.get(), 0, durationSV.get())
// optimisically set the progress bar // optimisically set the progress bar
seekProgressSV.set(newTime) seekProgressSV.set(newTime)
// it's seek by, so offset by the current time // it's seek by, so offset by the current time
// seekBy sets isSeekingSV back to false, so no need to do that here // seekBy sets isSeekingSV back to false, so no need to do that here
scheduleOnRN(seekBy, newTime - currentTimeSV.get()) scheduleOnRN(seekBy, newTime - currentTimeSV.get())
}) },
}, [ })
scrollGesture,
seekingAnimationSV,
seekBy,
screenWidth,
currentTimeSV,
durationSV,
isSeekingSV,
seekProgressSV,
])
const timeStyle = useAnimatedStyle(() => { const timeStyle = useAnimatedStyle(() => {
return { return {
+2 -2
View File
@@ -7,9 +7,9 @@ import {
View, View,
} from 'react-native' } from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type NativeGesture, type NativeGesture,
useNativeGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import Animated, { import Animated, {
useAnimatedStyle, useAnimatedStyle,
@@ -258,7 +258,7 @@ function Feed() {
const [currentIndex, setCurrentIndex] = useState(0) const [currentIndex, setCurrentIndex] = useState(0)
const scrollGesture = useMemo(() => Gesture.Native(), []) const scrollGesture = useNativeGesture()
const renderItem: ListRenderItem<VideoItem> = useCallback( const renderItem: ListRenderItem<VideoItem> = useCallback(
({item, index}) => { ({item, index}) => {
+19 -20
View File
@@ -1,19 +1,18 @@
import {createContext, useContext, useMemo, useRef, useState} from 'react' import {createContext, useContext, useMemo, useRef, useState} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import { import {
Gesture,
GestureDetector, GestureDetector,
type GestureStateChangeEvent, type PanGestureActiveEvent,
type GestureUpdateEvent, type PanGestureEvent,
type PanGestureHandlerEventPayload, usePanGesture,
} from 'react-native-gesture-handler' } from 'react-native-gesture-handler'
import {EventEmitter} from 'eventemitter3' import {EventEmitter} from 'eventemitter3'
export type GlobalGestureEvents = { export type GlobalGestureEvents = {
begin: GestureStateChangeEvent<PanGestureHandlerEventPayload> begin: PanGestureEvent
update: GestureUpdateEvent<PanGestureHandlerEventPayload> update: PanGestureActiveEvent
end: GestureStateChangeEvent<PanGestureHandlerEventPayload> end: PanGestureActiveEvent & {canceled: boolean}
finalize: GestureStateChangeEvent<PanGestureHandlerEventPayload> finalize: PanGestureEvent & {canceled: boolean}
} }
const Context = createContext<{ const Context = createContext<{
@@ -55,22 +54,22 @@ export function GlobalGestureEventsProvider({
}), }),
[events, setEnabled], [events, setEnabled],
) )
const gesture = Gesture.Pan() const gesture = usePanGesture({
.runOnJS(true) runOnJS: true,
.enabled(enabled) enabled,
.simultaneousWithExternalGesture() onBegin: e => {
.onBegin(e => {
events.emit('begin', e) events.emit('begin', e)
}) },
.onUpdate(e => { onUpdate: e => {
events.emit('update', e) events.emit('update', e)
}) },
.onEnd(e => { onDeactivate: e => {
events.emit('end', e) events.emit('end', e)
}) },
.onFinalize(e => { onFinalize: e => {
events.emit('finalize', e) events.emit('finalize', e)
}) },
})
return ( return (
<Context.Provider value={ctx}> <Context.Provider value={ctx}>