diff --git a/docs/superpowers/plans/2026-05-20-mobile-chat-thread-legendlist.md b/docs/superpowers/plans/2026-05-20-mobile-chat-thread-legendlist.md
new file mode 100644
index 000000000000..9624b998bfaf
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-20-mobile-chat-thread-legendlist.md
@@ -0,0 +1,338 @@
+# Mobile Chat Thread → LegendList Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Replace the mobile chat thread's inverted `FlatList` + `renderScrollComponent={KeyboardChatScrollView}` with `KeyboardChatLegendList` from `@legendapp/list/keyboard-chat`, matching the desktop LegendList implementation.
+
+**Architecture:** Switch from an inverted list (reversed ordinals, `KeyboardChatScrollView` as a render-prop scroll component) to a non-inverted `KeyboardChatLegendList` with natural ordinal order. Scroll stability is handled by `maintainScrollAtEnd` + `maintainVisibleContentPosition`. Separators move inline into `renderItem` (same pattern as desktop's `HighlightableRow`) because LegendList's `ItemSeparatorComponent` only exposes `leadingItem`, and non-inverted means we need the trailing-item ordinal for the orange-line logic.
+
+**Tech Stack:** `@legendapp/list/keyboard-chat` (`KeyboardChatLegendList`), `react-native-reanimated`, existing thread context hooks.
+
+**Base branch:** `nojima/HOTPOT-next-670-clean-2` (not `master`). Run `gh pr view --json baseRefName` before any `git diff` or comparison.
+
+**Working directory:** Always `cd shared/` first. Validate after every TypeScript change with `yarn lint` then `yarn tsc`.
+
+---
+
+## File Map
+
+| File | Change |
+|------|--------|
+| `shared/chat/conversation/messages/separator.tsx` | Remove `isMobile ? leadingItem : trailingItem` — always use `trailingItem` |
+| `shared/chat/conversation/list-area/index.tsx` | Rewrite `NativeConversationList`; remove helpers `useInvertedMessageOrdinals`, `useNativeSafeOnViewableItemsChanged`, `useNativeScrolling`, `RNFlatListRef`; clean dead imports |
+| `shared/chat/conversation/messages/special-top-message.tsx` | Remove mobile-only separator block (now handled inline by `NativeMobileRow`) |
+
+---
+
+### Task 1: Update separator to always use `trailingItem`
+
+The separator currently branches on `isMobile` because the inverted FlatList flipped which side of the separator held the "next" message. Non-inverted LegendList matches desktop: the separator renders above its row, so `trailingItem` (the current row's ordinal) is always correct.
+
+**Files:**
+- Modify: `shared/chat/conversation/messages/separator.tsx:17`
+
+- [ ] **Step 1: Edit separator.tsx**
+
+Change line 17 from:
+```tsx
+ const ordinal = isMobile ? leadingItem : trailingItem
+```
+to:
+```tsx
+ const ordinal = trailingItem
+```
+
+- [ ] **Step 2: Validate**
+
+```bash
+cd shared && yarn lint --quiet 2>&1 | head -30 && yarn tsc --noEmit 2>&1 | head -30
+```
+
+Expected: no errors related to separator.tsx.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd shared && git add chat/conversation/messages/separator.tsx
+git commit -m "separator: always use trailingItem now that mobile list is non-inverted"
+```
+
+---
+
+### Task 2: Rewrite NativeConversationList with KeyboardChatLegendList
+
+Replace the full mobile section of `index.tsx`. The new component mirrors the desktop `DesktopThreadWrapper` structure: stable-ref callbacks, a memoized row component that inlines the separator, `onViewableItemsChanged` for older-message loading (first 3 items visible → load), `onEndReached` for newer-message loading (only when `!containsLatestMessage`), and `KeyboardChatLegendList` with `initialScrollAtEnd` / `maintainScrollAtEnd` / `maintainVisibleContentPosition`.
+
+**Files:**
+- Modify: `shared/chat/conversation/list-area/index.tsx`
+
+- [ ] **Step 1: Replace the imports block**
+
+Remove these imports (no longer needed):
+```tsx
+import {FlatList} from 'react-native'
+import type {ScrollViewProps} from 'react-native'
+import {usingFlashList} from './flashlist-config'
+import {mobileTypingContainerHeight} from '../input-area/normal/typing'
+import {KeyboardChatScrollView} from 'react-native-keyboard-controller'
+```
+
+Add this import:
+```tsx
+import {KeyboardChatLegendList} from '@legendapp/list/keyboard-chat'
+```
+
+Keep all existing imports (LegendList from `@legendapp/list/react`, `useConversationThreadLoadNewerMessagesDueToScroll`, `useSafeAreaInsets`, `sortedIndexOf`, etc.).
+
+- [ ] **Step 2: Replace the entire native section**
+
+Delete everything from the comment `// ==================== NATIVE ====================` down to (but not including) the final `export default` line, and replace with:
+
+```tsx
+// ==================== NATIVE ====================
+
+const NativeMobileRow = React.memo(({ordinal}: {ordinal: T.Chat.Ordinal}) => {
+ const {centeredHighlightOrdinal} = useConversationCenter()
+ return (
+ <>
+
+
+ >
+ )
+})
+NativeMobileRow.displayName = 'NativeMobileRow'
+
+const NativeConversationList = function NativeConversationList() {
+ const conversationIDKey = useConversationThreadID()
+ const data = useConversationThreadSelector(
+ C.useShallow(s => ({
+ containsLatestMessage: !s.moreToLoadForward,
+ loaded: s.loaded,
+ messageOrdinals: s.messageOrdinals ?? noOrdinals,
+ }))
+ )
+ const {centeredOrdinal} = useConversationCenter()
+ const {containsLatestMessage, loaded, messageOrdinals} = data
+
+ const listRef = React.useRef(null)
+ const {markInitiallyLoadedThreadAsRead} = Hooks.useActions()
+ const loadOlderMessagesDueToScroll = useConversationThreadLoadOlderMessagesDueToScroll()
+ const loadNewerMessagesDueToScroll = useConversationThreadLoadNewerMessagesDueToScroll()
+ const getThreadLoadStatusOptions = useThreadLoadStatusOptionsGetter()
+ const threadStore = useConversationThreadStore()
+ const insets = useSafeAreaInsets()
+
+ // Stable refs for values used inside stable callbacks
+ const containsLatestMessageRef = React.useRef(containsLatestMessage)
+ React.useEffect(() => {
+ containsLatestMessageRef.current = containsLatestMessage
+ }, [containsLatestMessage])
+
+ const numOrdinalsRef = React.useRef(messageOrdinals.length)
+ React.useEffect(() => {
+ numOrdinalsRef.current = messageOrdinals.length
+ }, [messageOrdinals.length])
+
+ const messageOrdinalsRef = React.useRef(messageOrdinals)
+ React.useEffect(() => {
+ messageOrdinalsRef.current = messageOrdinals
+ }, [messageOrdinals])
+
+ const getItemType = React.useCallback(
+ (ordinal: T.Chat.Ordinal) => {
+ const {messageMap, messageTypeMap} = threadStore.getState()
+ const message = messageMap.get(ordinal)
+ return message ? getMessageRowType(message, messageTypeMap.get(ordinal)) : (messageTypeMap.get(ordinal) ?? 'text')
+ },
+ [threadStore]
+ )
+
+ const scrollToBottom = React.useCallback(() => {
+ void listRef.current?.scrollToEnd({animated: false})
+ }, [])
+
+ const {setScrollRef} = React.useContext(ScrollContext)
+ React.useEffect(() => {
+ setScrollRef({scrollDown: noop, scrollToBottom, scrollUp: noop})
+ }, [setScrollRef, scrollToBottom])
+
+ // Load older messages when scrolled near the top (first 3 items visible)
+ const onViewableItemsChanged = C.useDebouncedCallback(
+ ({viewableItems}: {viewableItems: Array<{index: number; item: T.Chat.Ordinal}>}) => {
+ if ((viewableItems[0]?.index ?? Infinity) < 3) {
+ loadOlderMessagesDueToScroll(numOrdinalsRef.current, getThreadLoadStatusOptions())
+ }
+ },
+ 200
+ )
+
+ // Load newer messages when scrolled to the end (only when not at latest)
+ const onEndReached = C.useThrottledCallback(() => {
+ if (!containsLatestMessageRef.current) {
+ loadNewerMessagesDueToScroll(numOrdinalsRef.current, getThreadLoadStatusOptions())
+ }
+ }, 200)
+
+ React.useEffect(
+ () => () => {
+ onEndReached.cancel()
+ },
+ [onEndReached]
+ )
+
+ // Scroll to centered ordinal when it changes (search / thread navigation)
+ const lastScrolledCenteredRef = React.useRef(undefined)
+ React.useLayoutEffect(() => {
+ lastScrolledCenteredRef.current = undefined
+ }, [conversationIDKey])
+
+ React.useEffect(() => {
+ if (!loaded) return
+ if (centeredOrdinal !== undefined) {
+ if (lastScrolledCenteredRef.current === centeredOrdinal) return
+ const idx = sortedIndexOf(
+ messageOrdinalsRef.current as unknown as number[],
+ centeredOrdinal as unknown as number
+ )
+ if (idx < 0) return
+ lastScrolledCenteredRef.current = centeredOrdinal
+ void listRef.current?.scrollToIndex({animated: false, index: idx, viewPosition: 0.5})
+ } else if (lastScrolledCenteredRef.current !== undefined) {
+ lastScrolledCenteredRef.current = undefined
+ if (containsLatestMessage) {
+ void listRef.current?.scrollToEnd({animated: false})
+ }
+ }
+ }, [centeredOrdinal, loaded, containsLatestMessage, messageOrdinals])
+
+ // Mark thread as read after initial load (once per conversation)
+ const markedReadRef = React.useRef(false)
+ React.useLayoutEffect(() => {
+ markedReadRef.current = false
+ }, [conversationIDKey])
+
+ const onLoad = React.useCallback(() => {
+ if (!markedReadRef.current) {
+ markedReadRef.current = true
+ markInitiallyLoadedThreadAsRead()
+ }
+ }, [markInitiallyLoadedThreadAsRead])
+
+ const renderItem = React.useCallback(
+ ({item: ordinal}: {item: T.Chat.Ordinal}) => ,
+ []
+ )
+
+ const jumpToRecent = Hooks.useJumpToRecent(scrollToBottom, messageOrdinals.length)
+
+ const _centeredIdx =
+ centeredOrdinal !== undefined
+ ? sortedIndexOf(messageOrdinals as unknown as number[], centeredOrdinal as unknown as number)
+ : -1
+ const initialScrollIndex =
+ _centeredIdx >= 0 ? {index: _centeredIdx, viewPosition: 0.5 as const} : undefined
+
+ return (
+
+
+
+ String(ordinal)}
+ getItemType={getItemType}
+ ListHeaderComponent={SpecialTopMessage}
+ ListFooterComponent={SpecialBottomMessage}
+ recycleItems={true}
+ drawDistance={250}
+ estimatedItemSize={72}
+ initialScrollAtEnd={initialScrollIndex === undefined}
+ initialScrollIndex={initialScrollIndex}
+ maintainScrollAtEnd={centeredOrdinal !== undefined ? false : {on: {dataChange: true}}}
+ maintainVisibleContentPosition={centeredOrdinal !== undefined ? undefined : {data: true}}
+ onLoad={onLoad}
+ onEndReached={onEndReached}
+ onViewableItemsChanged={onViewableItemsChanged as unknown as (info: unknown) => void}
+ keyboardDismissMode="on-drag"
+ keyboardShouldPersistTaps="handled"
+ offset={insets.bottom}
+ testID="messageList"
+ />
+ {jumpToRecent}
+
+
+
+ )
+}
+```
+
+- [ ] **Step 3: Remove now-dead helpers and clean imports**
+
+After replacing the native section, search the file for references to these and remove them if they no longer appear anywhere:
+
+- `useInvertedMessageOrdinals` function definition (was below the native comment)
+- `useNativeSafeOnViewableItemsChanged` function definition
+- `useNativeScrolling` function definition
+- `RNFlatListRef` type definition
+- `minTimeDelta` and `minDistanceFromEnd` constants
+- `maintainVisibleContentPosition` constant (the old `{autoscrollToTopThreshold: 1, minIndexForVisible: 0}` one)
+
+Also verify the import for `useConversationThreadLoadNewerMessagesDueToScroll` is present (it was already imported for desktop but double-check).
+
+- [ ] **Step 4: Validate**
+
+```bash
+cd shared && yarn lint --quiet 2>&1 | head -40 && yarn tsc --noEmit 2>&1 | head -40
+```
+
+Expected: no errors. Fix any type errors before proceeding.
+
+- [ ] **Step 5: Commit**
+
+```bash
+cd shared && git add chat/conversation/list-area/index.tsx
+git commit -m "mobile chat thread: replace inverted FlatList with KeyboardChatLegendList"
+```
+
+---
+
+### Task 3: Remove stale mobile separator from SpecialTopMessage
+
+`special-top-message.tsx` renders a special separator just for the inverted FlatList case (to show the orange line above the very first message). With non-inverted LegendList, `NativeMobileRow` handles this inline for every row including the first.
+
+**Files:**
+- Modify: `shared/chat/conversation/messages/special-top-message.tsx`
+
+- [ ] **Step 1: Remove the conditional separator block**
+
+Find and delete these lines (around line 181–184):
+```tsx
+ {!isMobile || usingFlashList ? null : (
+ // special case here with the sep. The flatlist and flashlist invert the leading-trailing, see useStateFast
+
+ )}
+```
+
+Also remove the now-unused import at the top:
+```tsx
+import {usingFlashList} from '../list-area/flashlist-config'
+```
+
+And remove the `Separator` import if it becomes unused after this deletion (check if it's used elsewhere in the file).
+
+- [ ] **Step 2: Validate**
+
+```bash
+cd shared && yarn lint --quiet 2>&1 | head -40 && yarn tsc --noEmit 2>&1 | head -40
+```
+
+Expected: no errors.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd shared && git add chat/conversation/messages/special-top-message.tsx
+git commit -m "special-top-message: remove stale mobile inverted-list separator"
+```
diff --git a/legends-ios.md b/legends-ios.md
new file mode 100644
index 000000000000..976c4002aeee
--- /dev/null
+++ b/legends-ios.md
@@ -0,0 +1,355 @@
+# LegendList iOS Chat Debugging
+
+## Problem Statement
+
+When scrolling to the top of the chat thread, older messages are loaded. When those messages arrive, some content briefly flashes at the top of the list for ~1 frame before settling into position. This is NOT the scroll position jump problem (that's fixed) — it's a visual glitch where incoming content overlays on top of existing content momentarily, then disappears/settles.
+
+**Current props of interest:**
+- `maintainVisibleContentPosition={{data: true, size: true}}`
+- `maintainScrollAtEnd={centeredOrdinal !== undefined ? false : {on: {dataChange: true}}}`
+- `alignItemsAtEnd={true}`
+- `recycleItems={false}`
+
+---
+
+## Experiments
+
+### Experiment 1: Simplified colored boxes + logging (current)
+**Hypothesis:** Real chat rows have complex rendering (images, reactions, etc.) that may interact badly with LegendList's measurement pass. Simplifying to fixed-size colored boxes will help isolate whether the flash is a measurement issue vs. a LegendList virtualization issue.
+
+**Changes:**
+- `DEBUG_LEGEND = true` in `list-area/index.tsx` swaps all rows for colored boxes with ordinal labels
+- Colors cycle by render index so every adjacent row is a different color
+- Heights are deterministic by ordinal: `[40, 55, 72, 90, 120, 48, 65, 100, 35, 160]px`
+- `ListHeaderComponent` replaced with a simple dark bar ("── LIST HEADER ──") to eliminate "Digging ancient messages..." as a variable
+- Index 0 item labeled "TOP: {ordinal}" with a black top border so the list boundary is always visible
+- Logging: `onStartReached`, data length changes (prepend), `totalSize` listener, `onLoad`, per-row `onLayout` (flags measured ≠ expected)
+
+**To test:** Scroll to the top of a long thread. Watch for flash. Also check Metro logs.
+
+**Observations:**
+- Flash still visible with debug boxes + simplified header
+- In image 3: white gap between 13858 and 13859, with 13859/13860 overlapping → confirmed flash is list-level, not chat-row rendering
+- In image 4: large white space above LIST HEADER bar → list has bounced past top when header is revealed
+
+**Status:** Ongoing → Experiment 2
+
+---
+
+### Experiment 2: Disable bounce during load (`bounces={false}` in debug mode)
+**Hypothesis:** iOS bounce animation is in-flight when new items arrive. `maintainVisibleContentPosition` applies a scroll offset adjustment that conflicts with the bounce physics, causing the 1-frame visual glitch.
+
+**Changes:**
+- `bounces={!DEBUG_LEGEND}` — bounce disabled when `DEBUG_LEGEND = true`
+
+**To test:** Scroll to top, let it bounce, watch for flash. If flash disappears → bounce is the culprit and we need to either disable bounce permanently or pause `onStartReached` while bouncing.
+
+**Observations:**
+- Flash is "a bit better" with bounces=false
+- **New problem:** when 99 items load in, all swipe momentum is killed dead
+
+**Log analysis from this run:**
+```
+onStartReached count=119 scroll=208.0 ← load triggered, user still moving fast
+totalSize=17526.4 scroll=61.7 ← LegendList internal resize (fires during render, before effects)
+ordinals 119→217 (+98) scroll=7985.0 ← React effect runs; MVCP already jumped scroll from 61 → 7985
+```
+- `totalSize` fires during the React render pass (synchronous), before the React `useEffect` runs — so by the time our effect records `scroll=7985`, the MVCP jump has already happened
+- The jump (61 → 7985) is ~7924px = height of 98 new prepended items — correct, but iOS kills momentum on any programmatic `contentOffset` change
+- `totalSize` fluctuates constantly while scrolling (9258→9340→9301→9278…) because each newly-visible item's measured size replaces its estimated size → each measurement triggers a micro-MVCP adjustment while the user is in motion
+
+**Root cause of velocity kill:** iOS cancels scroll momentum whenever `contentOffset` is set programmatically. The MVCP adjustment is a programmatic set. With `bounces=true` this was hidden because the bounce physics re-absorbed the jump; with `bounces=false` the kill is obvious.
+
+**Status:** Done — bounce re-enabled (velocity kill was masking, not causing the flash)
+
+---
+
+### Experiment 3: Uniform fixed-height boxes + exact estimatedItemSize
+**Hypothesis:** The constant `totalSize` fluctuation (9258→9340→9301…) during scrolling is caused by individual items being measured and replacing their estimated size. Each measurement triggers a micro-MVCP scroll adjustment while in motion, contributing to instability and possibly the flash. Making all boxes exactly `DEBUG_FIXED_HEIGHT=72` and setting `estimatedItemSize=72` eliminates measurement-driven adjustments entirely — if the flash disappears, size estimation noise is the culprit.
+
+**Changes:**
+- All debug boxes: uniform `height=72` (no variation)
+- `estimatedItemSize=72` (exact match — LegendList should never need to adjust for measurement delta)
+- `onLayout` only logs when measured height differs by >0.5px from expected (noise reduction)
+- `bounces` restored to default
+
+**To test:** Scroll to top, trigger load. Watch for flash. Also watch Metro for `totalSize` fluctuation — should now be stable jumps only (one big jump when items prepend, then flat).
+
+**Observations:**
+- Flash is visually better — measurement noise was a real contributor
+- `totalSize` now fires exactly once per load (no fluctuation) — confirmed uniform heights eliminate micro-adjustments
+- Log is clean: one `totalSize`, one `ordinals` change per load
+
+**Log pattern (both loads identical shape):**
+```
+onStartReached count=20 scroll=276.7
+totalSize=8568.0 scroll=119.0 ← during render; 119 items × 72 = 8568 ✓
+ordinals 20→119 (+99) scroll=7247.0 ← MVCP jumped 119→7247 (delta=7128 = 99×72 ✓)
+
+onStartReached count=119 scroll=337.3
+totalSize=15624.0 scroll=260.0 ← 217×72 = 15624 ✓
+ordinals 119→217 (+98) scroll=7316.0
+```
+- Delta is exact: 99×72=7128, scroll 119→7247=7128 ✓ — MVCP math is correct
+- The scroll at `totalSize` time (119, 260) shows the "danger window": new items are in LegendList but scroll hasn't been adjusted yet. This is the frame where the flash occurs.
+- Flash still present (better, not gone) → root cause is not measurement noise alone; the MVCP adjustment itself has a 1-frame lag where new items are positioned at the top while scroll is still at the old offset
+
+**Status:** Done → Experiment 4
+
+---
+
+### Experiment 4: Disable `maintainVisibleContentPosition` entirely
+**Hypothesis:** The 1-frame lag is caused by MVCP applying its scroll adjustment asynchronously (after commit). Disabling MVCP will remove the flash (but will cause scroll to jump to the top — terrible UX, but useful as a diagnostic). If flash disappears without MVCP, the flash is definitively caused by the async adjustment timing.
+
+**Changes (to try):** Set `maintainVisibleContentPosition={undefined}` regardless of centeredOrdinal.
+
+**To test:** Scroll up, trigger load, watch for flash. Expect scroll to jump, but does the flash disappear?
+
+**Observations:**
+- Can't keep MVCP off: without it, new items land at top while scroll stays put → non-contiguous history + runaway load loop (onStartReached keeps firing at scroll≈0)
+- **Root cause confirmed:** Without MVCP, `totalSize` and `ordinals` fire at the SAME scroll value (170.3, 170.3) — no jump. With MVCP on, they diverge (119 → 7247). The flash IS the 1-frame gap between LegendList committing new item positions and its async `scrollTo` adjustment landing.
+- Tiny subpixel totalSize drift (~0.1–0.4px) persists even with uniform 72px items — device pixel ratio rounding, not measurement noise.
+- MVCP restored.
+
+**Root cause summary:** LegendList's `maintainVisibleContentPosition` works by firing `scrollTo` *after* the native layout commit. For one native frame, new prepended items sit at positions 0–7128px while scroll is still at the old offset (~119px), making them briefly visible. The subsequent `scrollTo` to 7247 lands one frame later.
+
+**Status:** Done → root cause identified
+
+---
+
+### Experiment 5: Options to fix the 1-frame MVCP lag
+
+The flash is a 1-frame gap between LegendList rendering new items and its async `scrollTo` landing. Possible approaches:
+
+**A) Opacity shield:** When `onStartReached` fires, briefly set list opacity to 0. Restore after ordinals change + 1 frame. Hides the flash but adds a blink.
+
+**B) `setScrollProcessingEnabled(false)` around the update:** LegendList exposes this on the ref. If it defers scroll processing until re-enabled, it may batch the position adjustment atomically. Worth trying.
+
+**C) File a LegendList bug:** The async 1-frame lag in MVCP is arguably a LegendList issue. The fix would need to happen inside LegendList (e.g., pre-adjust scroll offset before committing new item layout). Worth opening an issue with this reproduction.
+
+**D) Pre-scroll before data arrives:** After `onStartReached`, immediately scroll to a large offset so that when items prepend, the visible area is already in safe territory. Hacky and fragile.
+
+**Status:** Implementing option C (patch)
+
+---
+
+### Experiment 5: Patch @legendapp/list to eliminate 1-frame RAF delay
+
+**Root cause confirmed:** `keyboard-chat.js` imports from `@legendapp/list/react-native.js`. That file's `ScrollAdjust` uses `Animated.View` and updates the anchor position via `requestAnimationFrame(flush)` — an explicit 1-frame deferral. This is why the anchor is always one frame late when new items are prepended.
+
+**Option B analysis:** `setScrollProcessingEnabled(false)` only gates the `onScroll` handler (`if (scrollProcessingEnabled === false) return;`) — it doesn't touch the MVCP/requestAdjust path at all. Skipped.
+
+**The patch (in `patches/@legendapp+list+3.0.0-beta.56.patch`):**
+
+In `react-native.js`'s `ScrollAdjust`:
+- Store `scrollOffset$` (the `Animated.Value`) and `bias` on `ctx.state`
+
+In `react-native.js`'s `ScrollAdjustHandler.requestAdjust`:
+- Before the `set$(scrollAdjust)` call (which triggers the RAF-deferred `setValue`), immediately call `animatedValue.setValue(newOffset)` synchronously
+- On New Architecture (JSI), `Animated.Value.setValue` updates the native UI thread before the next frame
+
+Also patched `index.native.js` similarly (not used by `keyboard-chat` but kept for consistency).
+
+**To test:** Scroll to top, trigger load. Flash should be gone.
+
+**Observations:**
+- Visual: "seems better, a little hard to tell" — flash reduced but may not be fully eliminated
+- Log pattern (with patch):
+ ```
+ onStartReached count=20 scroll=216.0
+ totalSize=8568.0 scroll=216.0 ← SAME scroll as trigger (was 119 before patch)
+ ordinals 20→119 (+99) scroll=7344.0 ← MVCP jump still present (delta=7128=99×72 ✓)
+ ```
+- Key change: `totalSize` now fires at scroll=216 (same as trigger), not at a lower value like 119. Before the patch, the user had scrolled further up between the trigger and the resize event. This suggests the synchronous `setValue` is changing native timing — the anchor view moves sooner, causing the MVCP scroll adjustment to happen faster.
+- `ordinals` still shows the 7128px MVCP jump — expected, MVCP is still working correctly.
+- The "danger window" appears to be shorter but may not be zero: new items are rendered → setValue fires synchronously → native processes anchor move → MVCP adjusts scroll. If setValue→MVCP path is fully synchronous (JSI), flash should be gone. If not fully atomic, 1 frame may remain.
+
+**Status:** Partially confirmed — visual improvement with debug boxes, worse with real items → Experiment 6
+
+---
+
+### Experiment 6: `maintainVisibleContentPosition={data: true, size: false}`
+
+**Root cause of "10 frames" with real items:** `size: true` triggers a MVCP `requestAdjust` call for every item whose measured height differs from `estimatedItemSize`. With 99 real items each measuring at different heights, each measurement fires: `requestAdjust` → sync `setValue` (our patch) → native MVCP (still 1 frame async). That's a continuous stream of 1-frame-lagged corrections across many frames — ~10 frames of instability instead of 1.
+
+Log evidence:
+```
+totalSize=7507.3 scroll=186.0
+scrollAdjust=5159.3 scroll=5345.3 ← initial data-prepend jump ✓
+totalSize=7521.8 scroll=5345.3
+scrollAdjust=5173.8 scroll=5359.8 ← measurement-driven +14.5px
+totalSize=7555.1 scroll=5358.8
+scrollAdjust=5208.1 scroll=5394.1 ← batched +35.3px
+... (continues for many more frames)
+```
+
+Debug boxes had this problem too (Exp 3) but was eliminated by exact estimatedItemSize — zero measurement deltas = no storm.
+
+**Change:** `maintainVisibleContentPosition={{data: true, size: false}}`
+
+- `data: true` — still fires one MVCP jump when items are prepended (correct)
+- `size: false` — stops MVCP from firing on every measurement delta (eliminates storm)
+
+**Tradeoff:** If items already in the list later change height (image loads, unfurls expanding), scroll position won't compensate. But those are rare, low-frequency events unlike the burst of 99 measurements at once.
+
+**To test:** Scroll to top, trigger load. Flash should be reduced to at most 1 frame (just the initial data jump).
+
+**Observations:** (fill in)
+
+**Observations:** "4 frames" with real items. Log shows scroll is now STABLE throughout all totalSize changes (size: false working). But 4-frame flash remains.
+
+**Root cause of remaining 4-frame flash:** LegendList warns "No unused container available... creating one on demand" with `numContainers: 20, numNeeded: 20, stillNeeded: 13`. After MVCP, 20 old item containers are all within drawDistance range → `findAvailableContainers` Pass 2 finds nothing reusable → 13 complex new-item containers created on demand. Rendering 13 complex message components (avatar, text, reactions) spreads across ~4 React Concurrent Mode time slices.
+
+Log also confirmed: LegendList uses `averageSizes[itemType].avg` (~52px, from measured old items) NOT `estimatedItemSize=72` for new item sizing.
+
+**Status:** Done → Experiment 7
+
+---
+
+### Experiment 7: `recycleItems={true}`
+
+**Hypothesis:** With `recycleItems={false}`, all 20 old item containers are in-range after MVCP → `findAvailableContainers` can't reuse any → creates 13 on demand. Rendering 13 new complex message components spreads across ~4 Concurrent Mode time slices → 4 frames of instability.
+
+With `recycleItems={true}`, old items 110-118 (at positions > drawDistance past viewport ~6389) ARE out of range after MVCP → their ~9 containers become reusable. On-demand creation drops from 13 to ~4. Fewer new renders → flash duration reduced.
+
+**Change:** `recycleItems={false}` → `recycleItems={true}`
+
+**Risk:** Recycled containers might briefly show wrong item content before React updates them ("recycling flash"). On New Architecture (Fabric), this should be minimal since view updates are synchronous within a commit.
+
+**To test:** Scroll to top, trigger load. Watch if flash is shorter or different character.
+
+**Observations:**
+- WORSE: breaks MVCP position maintenance. When new content arrives, existing items don't stay in their old spots — maintainVisibleContentPosition effectively stops working.
+- Reverted immediately to `recycleItems={false}`.
+
+**Root cause of regression:** LegendList's position maintenance relies on stable container-to-ordinal mapping. Recycling containers away from tracked ordinals breaks the scroll anchor.
+
+**Status:** Done → reverted → Experiment 8
+
+---
+
+### Experiment 8: Hide prepended items with `opacity: 0` during pre-MVCP window
+
+**Root cause to address:** During the pre-MVCP flash window, newly prepended items are rendered at estimated positions near the viewport (based on LegendList's average size ~52px) while scroll is still at the old position (~273px). For ~4 frames, both new and old items overlap in the viewport before MVCP fires to push scroll to ~5432px.
+
+**Insight:** The newly prepended items are ones the user has NEVER seen — they'll be above the viewport after MVCP fires. So hiding them during the flash window has zero UX cost: the user wasn't going to see them at the correct position anyway.
+
+**Approach:**
+- `PrependContext` — React context that carries the "cutoff ordinal" (the old `messageOrdinals[0]` before the prepend)
+- `useLayoutEffect` on `messageOrdinals` — fires before paint; reads `messageOrdinalsRef.current` (still holds previous render's value, since its `useEffect` hasn't run yet) to detect `currFirst < prevFirst`
+- Sets `prependedBeforeOrdinal = prevFirst` synchronously in the commit phase
+- `useEffect` with 150ms timeout clears it
+- `NativeMobileRow` reads `PrependContext` and applies `opacity: 0` when `ordinal < prependedBefore`
+- `renderItem` stays stable (no re-creation on prepend) — context bypasses `React.memo`
+
+**Changes:**
+- `PrependContext` added before `NativeMobileRow`
+- `NativeMobileRow` wraps in `Kb.Box2` with `style={isPrepended ? {opacity:0} : undefined}`
+- `prependedBeforeOrdinal` state + `useLayoutEffect` (detect) + `useEffect` (clear 150ms) in `NativeConversationList`
+- `PrependContext.Provider` wraps the list JSX
+
+**To test:** Scroll to top, trigger load. Flash should be gone (items hidden before they could be visible). Watch logs for "prepend detected" and "prepend opacity cleared" messages.
+
+**Observations:**
+- "Scrolling seems really jittery and busted... still pushes items down and doesn't maintain scroll position"
+- Root cause of jitter: the `PrependContext` caused ALL rendered NativeMobileRows to re-render twice per prepend (once when set, once cleared 150ms later). Even when `isPrepended=false` and the row's output is unchanged, the re-render + potential React Native native view update triggers `onLayout` bursts, throwing off LegendList's position calculations.
+- Reverted to Fragment return unconditionally, removed PrependContext entirely.
+
+**Status:** Done → reverted → Experiment 9
+
+---
+
+### Experiment 9: `drawDistance={500}` to eliminate on-demand container creation
+
+**Root cause of remaining 4-frame flash:** The container pool is sized at init using `numContainers = ceil((scrollLength + drawDistance*2) / avgItemSize)`. With `drawDistance=250` and avgItemSize≈72: `ceil((850+500)/72) = 19` → ~20 containers. Pool is then capped at `max(dataLength, numContainers)`. At init, `dataLength=20`, so pool=20.
+
+After prepend of 99 items, LegendList needs `numNeeded≈23` containers (viewport + drawDistance coverage). Pool has 20 → 13 must be created on demand via `findAvailableContainers` Pass 3. Each on-demand creation = new React component tree rendered in Concurrent Mode = spread across ~4 time slices.
+
+**Fix:** `drawDistance=500` gives:
+- `numContainers = ceil((850+1000)/72) = 26`
+- Pool at init = `max(20, 26) = 26`
+- After prepend: numNeeded≈23 < 26 → ZERO on-demand creations → no Concurrent Mode spreading → flash drops from 4 frames to ≤1 frame
+
+**Tradeoff:** 500px pre-rendered above+below viewport vs 250px. ~6 extra rows always rendered. Acceptable memory/CPU cost.
+
+**To test:** Scroll to top, trigger load. Watch for flash. Check for `"No unused container"` warning in Metro — it should be GONE.
+
+**Observations:**
+- WORSE — "things are flashing all over the place"
+- Root cause: `drawDistance=500` pre-renders 6 more rows (26 vs 20). Chat rows contain complex content. More pre-rendered rows = more `onLayout` events firing during normal scrolling from LegendList repositioning items (even when height is stable). This throws off LegendList's position calculations more frequently → jitter throughout scrolling, not just during prepend.
+- Added `onLayout` wrapper (DEBUG_LOG only) to detect row resizing: **confirmed NO ROW RESIZE events** — all rows are height-stable after first render. Images, unfurls, text all have fixed heights.
+- The `totalSize` oscillations come from LegendList's OWN container measurement of newly-rendered items (replacing averageSize estimates with real heights), not from our rows resizing.
+- Reverted `drawDistance` back to 250.
+
+**Key insight:** Increasing `drawDistance` increases pre-rendered row count, amplifying LegendList's internal measurement churn. Not the right lever.
+
+**Status:** Done → reverted → Experiment 10
+
+---
+
+### Experiment 10: Patch `getInitialContainerPoolSize` to remove `maxUsefulPoolSize` cap
+
+**Root cause:** `getInitialContainerPoolSize` caps the pool at `Math.min(maxUsefulPoolSize, targetPoolSize)` where `maxUsefulPoolSize = Math.max(dataLength, numContainers)`. With `dataLength=20` and `numContainers=20`, pool is always 20 — even though `initialContainerPoolRatio=3` (the default!) would give `targetPoolSize=60`.
+
+The cap exists to avoid "wasted" containers when data is small. But for a chat app that loads more items on scroll, those "spare" containers become essential immediately at the first scroll-back.
+
+**Fix:** Remove the `maxUsefulPoolSize` cap — just return `targetPoolSize`. With `numContainers=20` and `ratio=3`: pool=60 at mount. 40 are free (unassigned) containers. When 12 new items need containers after prepend: all 12 served from the free pool in Pass 1 of `findAvailableContainers`. No on-demand creation. No Concurrent Mode spreading. Flash drops from 4 frames to ≤1 frame.
+
+**Cost:** 40 extra container React components rendered at mount. Each renders null/empty (positioned off-screen at `top: -1e7`). Memory/CPU cost is negligible.
+
+**Patch:** `getInitialContainerPoolSize` in `react-native.js`: `return Math.min(maxUsefulPoolSize, targetPoolSize)` → `return targetPoolSize`
+
+**To test:** Scroll to top, trigger load. Watch for flash AND watch for `"No unused container"` warning — it should be GONE.
+
+**Observations:**
+- App crashes with OOM (EXC_BAD_ACCESS SIGSEGV) when scrolling up repeatedly
+- MALLOC: 1.5G across 1242 regions, VM_ALLOCATE: 512MB
+- Crash in `HermesRuntimeImpl::createStringFromAscii` → JSI OOM at JS layer
+- 40+ `com.apple.coremedia.sharedRootQueue` threads active in crash dump
+- This did NOT happen before our patches
+
+**Root cause investigation:**
+- Our patch went from pool=20 → pool=60 (40 extra Container React components always mounted)
+- Each Container has multiple `useArr$` subscriptions to the observable state
+- With recycleItems=false and container reuse, subscriptions may accumulate
+- Reverting pool fix first to isolate whether it's the culprit
+
+**Status:** Reverted (pool fix removed from react-native.js and patch file) → bisecting OOM
+
+---
+
+### Experiment 10b: OOM bisect — pool fix reverted, does OOM stop?
+
+**Change:** Reverted `return targetPoolSize` back to `return Math.min(maxUsefulPoolSize, targetPoolSize)` in `getInitialContainerPoolSize`. Pool is 20 again at mount.
+
+**Observations:** Still crashes. Pool fix was NOT the OOM cause.
+
+**Status:** Done → Animated.Value patch reverted next
+
+---
+
+### Experiment 10c: OOM bisect — all patches removed
+
+**Change:** Deleted patch file entirely, reverted both index.native.js and react-native.js to vanilla LegendList. Zero custom patches applied.
+
+**Observations:** Still crashes. OOM is a plain LegendList bug, not caused by any of our patches.
+
+**Root cause:** LegendList itself leaks memory when scrolling up repeatedly to load older messages. Likely related to how it accumulates native resources (CoreMedia threads from video content) in mounted containers as more items load. This is a LegendList upstream issue.
+
+**Status:** Stopping investigation. OOM is out of scope for this session — it's a LegendList bug to report upstream.
+
+---
+
+## Notes on LegendList internals
+
+- `scrollAdjust` — amount the list adjusted scroll offset to maintain position after a data change
+- `scrollAdjustPending` — adjustment queued but not yet applied (non-zero during the frame the flash could occur)
+- `maintainVisibleContentPosition: {data: true, size: true}` — restores position when data OR sizes change
+- `alignItemsAtEnd={true}` — short lists align to bottom (like a chat)
+
+## Key questions
+
+1. Does `scrollAdjustPending` spike to a non-zero value exactly when the flash occurs?
+2. Does the flash occur with simple fixed-height boxes (no measurement needed)?
+3. Does the flash occur with variable-height boxes?
+4. Does `estimatedItemSize` accuracy affect the flash?
diff --git a/shared/app/index.native.tsx b/shared/app/index.native.tsx
index 35ee779518d0..88a89f640e08 100644
--- a/shared/app/index.native.tsx
+++ b/shared/app/index.native.tsx
@@ -13,6 +13,7 @@ import {SafeAreaProvider, initialWindowMetrics} from 'react-native-safe-area-con
import {makeEngine} from '../engine'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import {enableFreeze} from 'react-native-screens'
+import {Image as ExpoImage} from 'expo-image'
import {setKeyboardUp} from '@/styles/keyboard-state'
import {setServiceDecoration} from '@/common-adapters/markdown/react'
import ServiceDecoration from '@/common-adapters/markdown/service-decoration'
@@ -27,6 +28,10 @@ logger.info('INIT App index module load')
enableFreeze(true)
setServiceDecoration(ServiceDecoration)
+// SDWebImage (used by expo-image) flushes its memory cache on iOS memory warnings, but
+// the simulator never sends memory warnings. Cap the cache so loading hundreds of chat
+// images doesn't exhaust VM in the simulator. On a real device this is a safety net only.
+ExpoImage.configureCache({maxMemoryCost: 100 * 1024 * 1024})
module.hot?.accept(() => {
console.log('accepted update in shared/index.native')
diff --git a/shared/chat/audio/audio-player.tsx b/shared/chat/audio/audio-player.tsx
index 6127f19e7dac..ab41861b3830 100644
--- a/shared/chat/audio/audio-player.tsx
+++ b/shared/chat/audio/audio-player.tsx
@@ -69,8 +69,13 @@ const AudioPlayer = (props: Props) => {
const {duration, big, maxWidth, url, visAmps} = props
const [playedRatio, setPlayedRatio] = React.useState(0)
const [paused, setPaused] = React.useState(true)
+ // Only mount AudioVideo after the user first taps play; calling useAudioPlayer
+ // unconditionally for every message in the list spawns CoreMedia threads per
+ // message and exhausts VM memory.
+ const [everPlayed, setEverPlayed] = React.useState(false)
const onClick = () => {
if (paused) {
+ setEverPlayed(true)
setPaused(false)
} else {
setPaused(true)
@@ -104,7 +109,7 @@ const AudioPlayer = (props: Props) => {
{formatAudioRecordDuration(timeLeft)}
- {url.length > 0 && (
+ {url.length > 0 && everPlayed && (
)}
diff --git a/shared/chat/conversation/list-area/flashlist-config.tsx b/shared/chat/conversation/list-area/flashlist-config.tsx
deleted file mode 100644
index 8c96234b47ef..000000000000
--- a/shared/chat/conversation/list-area/flashlist-config.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export const usingFlashList = false as boolean
diff --git a/shared/chat/conversation/list-area/index.tsx b/shared/chat/conversation/list-area/index.tsx
index a47dccecc3f9..e6a6ee313e67 100644
--- a/shared/chat/conversation/list-area/index.tsx
+++ b/shared/chat/conversation/list-area/index.tsx
@@ -2,7 +2,7 @@ import * as C from '@/constants'
import * as Kb from '@/common-adapters'
import * as Hooks from './hooks'
import * as React from 'react'
-import * as T from '@/constants/types'
+import type * as T from '@/constants/types'
import Separator from '../messages/separator'
import SpecialBottomMessage from '../messages/special-bottom-message'
import SpecialTopMessage from '../messages/special-top-message'
@@ -26,13 +26,9 @@ import {FocusContext} from '../normal/context'
import noop from 'lodash/noop'
import {LegendList} from '@legendapp/list/react'
import type {LegendListRef} from '@/common-adapters'
-import {FlatList} from 'react-native'
-import type {ScrollViewProps} from 'react-native'
-import {usingFlashList} from './flashlist-config'
-import {mobileTypingContainerHeight} from '../input-area/normal/typing'
-import {KeyboardChatScrollView} from 'react-native-keyboard-controller'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
-import type {ItemType} from './index.shared'
+import {KeyboardChatLegendList} from '@legendapp/list/keyboard-chat'
+import {useSharedValue} from 'react-native-reanimated'
const noOrdinals: ReadonlyArray = []
@@ -377,295 +373,285 @@ const DesktopThreadWrapperWithProfiler = () => (
// ==================== NATIVE ====================
-type RNFlatListRef = {
- scrollToOffset: (opts: {animated: boolean; offset: number}) => void
- scrollToItem: (opts: {animated: boolean; item: unknown; viewPosition?: number}) => void
+// Toggle to replace real rows with colored debug boxes.
+const DEBUG_LEGEND = false
+// Toggle extra logging (independent of debug boxes).
+const DEBUG_LOG = false
+
+const DEBUG_FIXED_HEIGHT = 72
+const debugColors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#F0A500', '#98D8C8'] as const
+// XOR-fold so adjacent ordinals (which differ by ~1) land on different color buckets
+const debugColor = (o: T.Chat.Ordinal) => {
+ const n = Number(o)
+ return debugColors[((n ^ (n >> 3) ^ (n >> 7)) & 0xff) % debugColors.length] ?? '#ccc'
}
-const useInvertedMessageOrdinals = (messageOrdinals?: ReadonlyArray) => {
- const source = messageOrdinals ?? noOrdinals
- return React.useMemo(() => (source.length > 1 ? [...source].reverse() : source), [source])
-}
-
-const useNativeScrolling = (p: {
- centeredOrdinal: T.Chat.Ordinal
- messageOrdinals: ReadonlyArray
- conversationIDKey: T.Chat.ConversationIDKey
- listRef: React.RefObject
-}) => {
- const {listRef, centeredOrdinal, messageOrdinals} = p
- const numOrdinals = messageOrdinals.length
- const loadOlderMessages = useConversationThreadLoadOlderMessagesDueToScroll()
- const getThreadLoadStatusOptions = useThreadLoadStatusOptionsGetter()
- const [scrollToBottom] = React.useState(() => () => {
- listRef.current?.scrollToOffset({animated: false, offset: 0})
- })
-
- const {setScrollRef} = React.useContext(ScrollContext)
- React.useEffect(() => {
- setScrollRef({scrollDown: noop, scrollToBottom, scrollUp: noop})
- }, [setScrollRef, scrollToBottom])
-
- // only scroll to center once per
- const lastScrollToCentered = React.useRef(-1)
- React.useEffect(() => {
- if (T.Chat.ordinalToNumber(centeredOrdinal) < 0) {
- lastScrollToCentered.current = -1
- }
- }, [centeredOrdinal])
-
- const centeredOrdinalRef = React.useRef(centeredOrdinal)
- React.useEffect(() => {
- centeredOrdinalRef.current = centeredOrdinal
- }, [centeredOrdinal])
- const [scrollToCentered] = React.useState(() => () => {
- setTimeout(() => {
- const list = listRef.current
- if (!list) {
- return
- }
- const co = centeredOrdinalRef.current
- if (lastScrollToCentered.current === co) {
- return
- }
-
- lastScrollToCentered.current = co
- list.scrollToItem({animated: false, item: co, viewPosition: 0.5})
- }, 100)
- })
-
- const onEndReached = () => {
- loadOlderMessages(numOrdinals, getThreadLoadStatusOptions())
- }
+const DebugTopHeader = () => (
+
+ ── LIST HEADER ──
+
+)
- return {
- onEndReached,
- scrollToBottom,
- scrollToCentered,
- }
-}
+const DebugRow = React.memo(({ordinal, index}: {ordinal: T.Chat.Ordinal; index: number}) => {
+ const color = debugColor(ordinal)
+ const isTop = index === 0
+ return (
+ {
+ const measured = e.nativeEvent.layout.height
+ if (Math.abs(measured - DEBUG_FIXED_HEIGHT) > 0.5) {
+ console.log(`[LegendDebug] DebugRow ordinal=${ordinal} expected=${DEBUG_FIXED_HEIGHT} measured=${measured}`)
+ }
+ }}
+ >
+ {isTop ? `TOP: ${ordinal}` : String(ordinal)}
+
+ )
+})
+DebugRow.displayName = 'DebugRow'
-// This keeps the list stable when data changes. If we don't do this it will jump around
-// when new messages come in and its very easy to get this to cause an unstoppable loop of
-// quick janking up and down
-const maintainVisibleContentPosition = {autoscrollToTopThreshold: 1, minIndexForVisible: 0}
+const NativeMobileRow = React.memo(({ordinal}: {ordinal: T.Chat.Ordinal}) => {
+ const {centeredHighlightOrdinal} = useConversationCenter()
+ return (
+ <>
+
+
+ >
+ )
+})
+NativeMobileRow.displayName = 'NativeMobileRow'
const NativeConversationList = function NativeConversationList() {
- const List = FlatList as unknown as React.ComponentType & {ref?: React.Ref}>
-
- const debugWhichList = __DEV__ ? (
-
- {usingFlashList ? 'FLASH' : 'old'}
-
- ) : null
-
const conversationIDKey = useConversationThreadID()
- const listData = useConversationThreadSelector(
+ const data = useConversationThreadSelector(
C.useShallow(s => ({
+ containsLatestMessage: !s.moreToLoadForward,
loaded: s.loaded,
- messageOrdinals: s.messageOrdinals,
+ messageOrdinals: s.messageOrdinals ?? noOrdinals,
}))
)
- const {centeredHighlightOrdinal, centeredOrdinal} = useConversationCenter()
- const noCenteredOrdinal = T.Chat.numberToOrdinal(-1)
- const centeredOrdinalOrNone = centeredOrdinal ?? noCenteredOrdinal
- const centeredHighlightOrdinalOrNone = centeredHighlightOrdinal ?? noCenteredOrdinal
- const {loaded} = listData
-
- const messageOrdinals = useInvertedMessageOrdinals(listData.messageOrdinals)
+ const {centeredOrdinal} = useConversationCenter()
+ const {containsLatestMessage, loaded, messageOrdinals} = data
- const listRef = React.useRef(null)
+ const listRef = React.useRef | null>(null)
const {markInitiallyLoadedThreadAsRead} = Hooks.useActions()
- const keyExtractor = (ordinal: ItemType) => {
- return String(ordinal)
- }
+ const loadOlderMessagesDueToScroll = useConversationThreadLoadOlderMessagesDueToScroll()
+ const loadNewerMessagesDueToScroll = useConversationThreadLoadNewerMessagesDueToScroll()
+ const getThreadLoadStatusOptions = useThreadLoadStatusOptionsGetter()
+ const threadStore = useConversationThreadStore()
+ const insets = useSafeAreaInsets()
+ const contentInsetEndAdjustment = useSharedValue(insets.bottom)
+ React.useEffect(() => {
+ contentInsetEndAdjustment.value = insets.bottom
+ }, [contentInsetEndAdjustment, insets.bottom])
- const renderItem = (info?: {item?: ItemType}) => {
- const ordinal = info?.item
- if (!ordinal) {
- return null
+ // Stable refs for values used inside stable callbacks
+ const containsLatestMessageRef = React.useRef(containsLatestMessage)
+ React.useEffect(() => {
+ containsLatestMessageRef.current = containsLatestMessage
+ }, [containsLatestMessage])
+
+ const numOrdinalsRef = React.useRef(messageOrdinals.length)
+ React.useEffect(() => {
+ const prev = numOrdinalsRef.current
+ const curr = messageOrdinals.length
+ if (DEBUG_LOG && prev !== curr) {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ const prevFirst = messageOrdinalsRef.current[0]
+ const currFirst = messageOrdinals[0]
+ const prependInfo = currFirst !== prevFirst ? ` firstOrd=${prevFirst}→${currFirst}` : ''
+ console.log(`[LegendDebug] ordinals ${prev}→${curr} (+${curr - prev})${prependInfo} scroll=${scroll.toFixed(1)}`)
}
- return
- }
+ numOrdinalsRef.current = messageOrdinals.length
+ }, [messageOrdinals.length])
- const numOrdinals = messageOrdinals.length
+ const messageOrdinalsRef = React.useRef(messageOrdinals)
+ React.useEffect(() => {
+ messageOrdinalsRef.current = messageOrdinals
+ }, [messageOrdinals])
- const threadStore = useConversationThreadStore()
const getItemType = React.useCallback(
(ordinal: T.Chat.Ordinal) => {
- if (!ordinal) {
- return 'null'
- }
const {messageMap, messageTypeMap} = threadStore.getState()
const message = messageMap.get(ordinal)
- return message
- ? getMessageRowType(message, messageTypeMap.get(ordinal))
- : (messageTypeMap.get(ordinal) ?? 'text')
+ return message ? getMessageRowType(message, messageTypeMap.get(ordinal)) : (messageTypeMap.get(ordinal) ?? 'text')
},
[threadStore]
)
- const insets = useSafeAreaInsets()
+ const scrollToBottom = React.useCallback(() => {
+ void listRef.current?.scrollToEnd({animated: false})
+ }, [])
- const {scrollToCentered, scrollToBottom, onEndReached} = useNativeScrolling({
- centeredOrdinal: centeredOrdinalOrNone,
- conversationIDKey,
- listRef,
- messageOrdinals,
- })
+ const {setScrollRef} = React.useContext(ScrollContext)
+ React.useEffect(() => {
+ setScrollRef({scrollDown: noop, scrollToBottom, scrollUp: noop})
+ }, [setScrollRef, scrollToBottom])
- const jumpToRecent = Hooks.useJumpToRecent(scrollToBottom, messageOrdinals.length)
+ const onStartReached = C.useThrottledCallback(() => {
+ if (DEBUG_LOG) {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ console.log(`[LegendDebug] onStartReached count=${numOrdinalsRef.current} scroll=${scroll.toFixed(1)}`)
+ }
+ loadOlderMessagesDueToScroll(numOrdinalsRef.current, getThreadLoadStatusOptions())
+ }, 200)
- const lastCenteredOrdinal = React.useRef(0)
- React.useEffect(() => {
- if (lastCenteredOrdinal.current === centeredOrdinalOrNone) {
- return
+ React.useEffect(
+ () => () => {
+ onStartReached.cancel()
+ },
+ [onStartReached]
+ )
+
+ // Load newer messages when scrolled to the end (only when not at latest)
+ const onEndReached = C.useThrottledCallback(() => {
+ if (!containsLatestMessageRef.current) {
+ loadNewerMessagesDueToScroll(numOrdinalsRef.current, getThreadLoadStatusOptions())
}
- lastCenteredOrdinal.current = centeredOrdinalOrNone
- if (centeredOrdinalOrNone > 0) {
- const id = setTimeout(() => {
- scrollToCentered()
- }, 200)
- return () => {
- clearTimeout(id)
+ }, 200)
+
+ React.useEffect(
+ () => () => {
+ onEndReached.cancel()
+ },
+ [onEndReached]
+ )
+
+ // Scroll to centered ordinal when it changes (search / thread navigation)
+ const lastScrolledCenteredRef = React.useRef(undefined)
+ React.useLayoutEffect(() => {
+ lastScrolledCenteredRef.current = undefined
+ }, [conversationIDKey])
+
+ React.useEffect(() => {
+ if (!loaded) return
+ if (centeredOrdinal !== undefined) {
+ if (lastScrolledCenteredRef.current === centeredOrdinal) return
+ const idx = sortedIndexOf(
+ messageOrdinalsRef.current as unknown as number[],
+ centeredOrdinal as unknown as number
+ )
+ if (idx < 0) return
+ lastScrolledCenteredRef.current = centeredOrdinal
+ void listRef.current?.scrollToIndex({animated: false, index: idx, viewPosition: 0.5})
+ } else if (lastScrolledCenteredRef.current !== undefined) {
+ lastScrolledCenteredRef.current = undefined
+ if (containsLatestMessage) {
+ void listRef.current?.scrollToEnd({animated: false})
}
}
- return undefined
- }, [centeredOrdinalOrNone, scrollToCentered])
+ }, [centeredOrdinal, loaded, containsLatestMessage, messageOrdinals])
- const prevLoadedRef = React.useRef(false)
- const markedLoadedThreadRef = React.useRef(false)
+ // Mark thread as read after initial load (once per conversation)
+ const markedReadRef = React.useRef(false)
React.useLayoutEffect(() => {
- prevLoadedRef.current = false
- markedLoadedThreadRef.current = false
+ markedReadRef.current = false
}, [conversationIDKey])
- React.useLayoutEffect(() => {
- const justLoaded = loaded && !prevLoadedRef.current
- prevLoadedRef.current = loaded
- if (!justLoaded) return
+ React.useEffect(() => {
+ if (!DEBUG_LOG) return
+ const unsubs: Array<() => void> = []
+ const t = setTimeout(() => {
+ const state = listRef.current?.getState()
+ if (!state) return
+ unsubs.push(state.listen('totalSize', v => {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ console.log(`[LegendDebug] totalSize=${v.toFixed(1)} scroll=${scroll.toFixed(1)}`)
+ }))
+ unsubs.push(state.listen('scrollAdjust', v => {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ console.log(`[LegendDebug] scrollAdjust=${(v ?? 0).toFixed(1)} scroll=${scroll.toFixed(1)}`)
+ }))
+ unsubs.push(state.listen('scrollAdjustPending', v => {
+ if (v) {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ console.log(`[LegendDebug] scrollAdjustPending=${v.toFixed(1)} scroll=${scroll.toFixed(1)}`)
+ }
+ }))
+ console.log('[LegendDebug] state listeners attached')
+ }, 300)
+ return () => {
+ clearTimeout(t)
+ unsubs.forEach(fn => fn())
+ }
+ }, [])
- if (!markedLoadedThreadRef.current) {
- markedLoadedThreadRef.current = true
+ const onLoad = React.useCallback(() => {
+ if (DEBUG_LOG) {
+ const scroll = listRef.current?.getState().scroll ?? 0
+ console.log(`[LegendDebug] onLoad scroll=${scroll.toFixed(1)}`)
+ }
+ if (!markedReadRef.current) {
+ markedReadRef.current = true
markInitiallyLoadedThreadAsRead()
}
+ }, [markInitiallyLoadedThreadAsRead])
- if (centeredOrdinalOrNone > 0) {
- scrollToCentered()
- setTimeout(() => {
- scrollToCentered()
- }, 100)
- } else if (numOrdinals > 0) {
- scrollToBottom()
- setTimeout(() => {
- scrollToBottom()
- }, 100)
- }
- }, [
- centeredOrdinalOrNone,
- loaded,
- markInitiallyLoadedThreadAsRead,
- numOrdinals,
- scrollToBottom,
- scrollToCentered,
- ])
-
- const onViewableItemsChanged = useNativeSafeOnViewableItemsChanged(onEndReached, messageOrdinals.length)
-
- const renderScrollComponent = React.useCallback(
- (props: ScrollViewProps) => (
-
- ),
- [insets.bottom]
+ const renderItem = React.useCallback(
+ DEBUG_LEGEND
+ ? ({item: ordinal, index}: {item: T.Chat.Ordinal; index: number}) =>
+ : ({item: ordinal}: {item: T.Chat.Ordinal}) => ,
+ []
)
- const nativeContentContainerStyle = React.useMemo(
- () => ({
- paddingBottom: 0,
- paddingTop: mobileTypingContainerHeight + insets.bottom,
- }),
- [insets.bottom]
- )
+ const jumpToRecent = Hooks.useJumpToRecent(scrollToBottom, messageOrdinals.length)
+
+ const _centeredIdx =
+ centeredOrdinal !== undefined
+ ? sortedIndexOf(messageOrdinals as unknown as number[], centeredOrdinal as unknown as number)
+ : -1
+ const initialScrollIndex =
+ _centeredIdx >= 0 ? {index: _centeredIdx, viewPosition: 0.5 as const} : undefined
return (
- String(ordinal)}
+ getItemType={getItemType}
+ ListHeaderComponent={DEBUG_LEGEND ? DebugTopHeader : SpecialTopMessage}
+ ListFooterComponent={SpecialBottomMessage}
+ recycleItems={false}
+ drawDistance={250} // limits simultaneously-mounted items (analog to FlatList windowSize=3)
+ estimatedItemSize={DEBUG_LEGEND ? DEBUG_FIXED_HEIGHT : 72}
+ initialScrollAtEnd={initialScrollIndex === undefined}
+ initialScrollIndex={initialScrollIndex}
+ alignItemsAtEnd={true}
+ maintainScrollAtEnd={centeredOrdinal !== undefined ? false : {on: {dataChange: true}}}
+ maintainVisibleContentPosition={centeredOrdinal !== undefined ? undefined : {data: true, size: false}}
+ onLoad={onLoad}
+ onStartReached={onStartReached}
+ onEndReached={onEndReached}
+ keyboardDismissMode="on-drag"
+ keyboardShouldPersistTaps="handled"
+ contentInsetEndAdjustment={contentInsetEndAdjustment}
+ offset={insets.bottom}
+ testID="messageList"
/>
{jumpToRecent}
- {debugWhichList}
)
}
-const minTimeDelta = 1000
-const minDistanceFromEnd = 10
-
-const useNativeSafeOnViewableItemsChanged = (onEndReached: () => void, numOrdinals: number) => {
- const nextCallbackRef = React.useRef(new Date().getTime())
- const onEndReachedRef = React.useRef(onEndReached)
- React.useEffect(() => {
- onEndReachedRef.current = onEndReached
- }, [onEndReached])
- const numOrdinalsRef = React.useRef(numOrdinals)
- React.useEffect(() => {
- numOrdinalsRef.current = numOrdinals
- nextCallbackRef.current = new Date().getTime() + minTimeDelta
- }, [numOrdinals])
-
- // this can't change ever, so we have to use refs to keep in sync
- const onViewableItemsChanged = React.useRef(
- ({viewableItems}: {viewableItems: Array<{index: number | null}>}) => {
- const idx = viewableItems.at(-1)?.index ?? 0
- const lastIdx = numOrdinalsRef.current - 1
- const offset = numOrdinalsRef.current > 50 ? minDistanceFromEnd : 1
- const deltaIdx = idx - lastIdx + offset
- // not far enough from the end
- if (deltaIdx < 0) {
- return
- }
- const t = new Date().getTime()
- const deltaT = t - nextCallbackRef.current
- // enough time elapsed?
- if (deltaT > 0) {
- nextCallbackRef.current = t + minTimeDelta
- onEndReachedRef.current()
- }
- }
- )
- return onViewableItemsChanged
-}
-
export default isMobile ? NativeConversationList : DesktopThreadWrapperWithProfiler
diff --git a/shared/chat/conversation/messages/attachment/video/videoimpl.tsx b/shared/chat/conversation/messages/attachment/video/videoimpl.tsx
index 52207a2b4b42..dd9f23b1def8 100644
--- a/shared/chat/conversation/messages/attachment/video/videoimpl.tsx
+++ b/shared/chat/conversation/messages/attachment/video/videoimpl.tsx
@@ -53,31 +53,60 @@ const DesktopVideoImpl = (p: Props) => {
)
}
-const NativeVideoImpl = (p: Props) => {
- const {allowPlay, message, showPopup} = p
- const {fileURL: url, transferState, videoDuration} = message
- const {previewURL, height, width} = getAttachmentPreviewSize(message)
- const {showPoster, reveal} = usePosterState(url)
- const sourceUri = `${url}&contentforce=true`
+// Separated into its own component so useVideoPlayer is only called when the
+// user actually taps play. Calling useVideoPlayer unconditionally for every
+// video message in the list caused CoreMedia to initialize a player per
+// message, spawning dozens of network threads and exhausting VM memory.
+type NativeActiveVideoProps = {
+ sourceUri: string
+ height: number
+ width: number
+}
+const NativeActiveVideo = (p: NativeActiveVideoProps) => {
+ const {sourceUri, height, width} = p
const player = useVideoPlayer(sourceUri, pl => {
pl.loop = false
+ pl.play()
})
-
- const onPress = () => {
- reveal()
- player.play()
- }
-
useEventListener(player, 'playToEnd', () => {
player.replay()
})
+ return (
+
+ )
+}
+
+const NativeVideoImpl = (p: Props) => {
+ const {allowPlay, message, showPopup} = p
+ const {fileURL: url, transferState, videoDuration} = message
+ const {previewURL, height, width} = getAttachmentPreviewSize(message)
+ const [playerActive, setPlayerActive] = React.useState(false)
+ const [lastUrl, setLastUrl] = React.useState(url)
+ if (lastUrl !== url) {
+ setLastUrl(url)
+ setPlayerActive(false)
+ }
+ const sourceUri = `${url}&contentforce=true`
return (
<>
- {showPoster ? (
-
+ {playerActive && url ? (
+
+ ) : (
+ {
+ if (allowPlay) setPlayerActive(true)
+ }}
+ style={nativeStyles.pressable}
+ onLongPress={showPopup}
+ >
{
- ) : (
-
)}
>
)
diff --git a/shared/chat/conversation/messages/separator.tsx b/shared/chat/conversation/messages/separator.tsx
index 96b45b21422c..684ef10a373d 100644
--- a/shared/chat/conversation/messages/separator.tsx
+++ b/shared/chat/conversation/messages/separator.tsx
@@ -13,8 +13,8 @@ const missingMessage = Chat.makeMessageDeleted({})
const noOrdinal = T.Chat.numberToOrdinal(0)
// Single merged selector replacing useStateFast + useState
-const useSeparatorData = (trailingItem: T.Chat.Ordinal, leadingItem: T.Chat.Ordinal) => {
- const ordinal = isMobile ? leadingItem : trailingItem
+const useSeparatorData = (trailingItem: T.Chat.Ordinal) => {
+ const ordinal = trailingItem
const orangeOrdinal = React.useContext(OrangeLineContext)
const you = useCurrentUserState(s => s.username)
@@ -49,13 +49,12 @@ const useSeparatorData = (trailingItem: T.Chat.Ordinal, leadingItem: T.Chat.Ordi
}
type Props = {
- leadingItem?: T.Chat.Ordinal
trailingItem: T.Chat.Ordinal
}
function SeparatorConnector(p: Props) {
- const {leadingItem, trailingItem} = p
- const data = useSeparatorData(trailingItem, leadingItem ?? T.Chat.numberToOrdinal(0))
+ const {trailingItem} = p
+ const data = useSeparatorData(trailingItem)
const {ordinal, orangeLineAbove, orangeTime} = data
if (!ordinal || !orangeLineAbove) return null
diff --git a/shared/chat/conversation/messages/special-top-message.tsx b/shared/chat/conversation/messages/special-top-message.tsx
index 0a5d086f375e..bd64170f2925 100644
--- a/shared/chat/conversation/messages/special-top-message.tsx
+++ b/shared/chat/conversation/messages/special-top-message.tsx
@@ -1,7 +1,6 @@
import * as C from '@/constants'
import * as T from '@/constants/types'
import * as Kb from '@/common-adapters'
-import Separator from './separator'
import HelloBotCard from './cards/hello-bot'
import MakeTeamCard from './cards/make-team'
import NewChatCard from './cards/new-chat'
@@ -12,7 +11,6 @@ import {
useConversationThreadID,
useConversationThreadSelector,
} from '../thread-context'
-import {usingFlashList} from '../list-area/flashlist-config'
import * as FS from '@/constants/fs'
import {useCurrentUserState} from '@/stores/current-user'
@@ -112,9 +110,8 @@ const ErrorMessage = () => {
function SpecialTopMessage() {
const username = useCurrentUserState(s => s.username)
const conversationIDKey = useConversationThreadID()
- const {firstOrdinal, hasLoadedEver, meta, moreToLoadBack, participants} = useConversationThreadSelector(
+ const {hasLoadedEver, meta, moreToLoadBack, participants} = useConversationThreadSelector(
C.useShallow(s => ({
- firstOrdinal: s.messageOrdinals?.[0] ?? T.Chat.numberToOrdinal(0),
hasLoadedEver: s.messageOrdinals !== undefined,
meta: s.meta,
moreToLoadBack: s.moreToLoadBack,
@@ -178,10 +175,6 @@ function SpecialTopMessage() {
Digging ancient messages...
)}
- {!isMobile || usingFlashList ? null : (
- // special case here with the sep. The flatlist and flashlist invert the leading-trailing, see useStateFast
-
- )}
)
}
diff --git a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.tsx b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.tsx
index cc0ef41e7069..37c2424dd184 100644
--- a/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.tsx
+++ b/shared/chat/conversation/messages/text/unfurl/unfurl-list/image/video.tsx
@@ -53,12 +53,18 @@ const DesktopVideo = (p: Props) => {
)
}
-const NativeVideo = (props: Props) => {
- const {autoPlay, onClick, url, style, width, height} = props
- const {playing, setPlaying} = usePlayState(url, autoPlay)
+// Separated into its own component so useVideoPlayer is only called when the
+// player is needed. Calling it unconditionally for every unfurl in the list
+// spawns CoreMedia threads per message and exhausts VM memory.
+type NativeActiveVideoProps = {
+ sourceUri: string
+ autoPlay: boolean
+ playing: boolean
+ style: object
+}
- const uri = url.length > 0 ? url : 'https://'
- const sourceUri = `${uri}&autoplay=${autoPlay ? 'true' : 'false'}&contentforce=true`
+const NativeActiveVideo = (props: NativeActiveVideoProps) => {
+ const {sourceUri, autoPlay, playing, style} = props
const player = useVideoPlayer(sourceUri, p => {
p.loop = true
@@ -85,22 +91,50 @@ const NativeVideo = (props: Props) => {
return () => sub.remove()
}, [player])
+ return (
+
+ )
+}
+
+const NativeVideo = (props: Props) => {
+ const {autoPlay, onClick, url, style, width, height} = props
+ const {playing, setPlaying} = usePlayState(url, autoPlay)
+ // Activate the player when autoPlay is true or the user first taps play.
+ // Reset when URL changes so a new source gets a fresh player.
+ const [active, setActive] = React.useState(autoPlay)
+ const [lastUrl, setLastUrl] = React.useState(url)
+ if (lastUrl !== url) {
+ setLastUrl(url)
+ setActive(autoPlay)
+ }
+
+ const uri = url.length > 0 ? url : 'https://'
+ const sourceUri = `${uri}&autoplay=${autoPlay ? 'true' : 'false'}&contentforce=true`
+
const _onClick = () => {
if (onClick) {
onClick()
return
}
+ setActive(true)
setPlaying(p => !p)
}
return (
-
+ {active && (
+
+ )}
{!playing && }
diff --git a/shared/desktop/electron-sums.mts b/shared/desktop/electron-sums.mts
index 964e887ae061..4a826bb4b47e 100644
--- a/shared/desktop/electron-sums.mts
+++ b/shared/desktop/electron-sums.mts
@@ -1,10 +1,10 @@
// Generated with: ./extract-electron-shasums.sh {ver}
// prettier-ignore
export const electronChecksums = {
- 'electron-v42.1.0-darwin-arm64.zip': '98d097299eb08094d0df3312b2d6e8677069d8defdab891143628d4f82f46117',
- 'electron-v42.1.0-darwin-x64.zip': '63b938cbe6696f67f172d8f7cb6c31a58c38853d03d33f047fcba8c628cc700f',
- 'electron-v42.1.0-linux-arm64.zip': '1e700f7f3daef794cc45235e51c1172664aed49a4e7737b8896ddc398bff4d7d',
- 'electron-v42.1.0-linux-x64.zip': '882047343a9e203c6cfc5d39b166ea9e025dd256943e0d3711f86725ad0e3bd9',
- 'electron-v42.1.0-win32-x64.zip': '0b03582d0a68dce8473fcc090114dabef7eaafd52b6d8cd2c85b000358c6af31',
- 'hunspell_dictionaries.zip': '2ec14898860936c418abcb6bcc0d9910a3889dc364b4cef0bab583d94f9f2806',
+ 'electron-v42.2.0-darwin-arm64.zip': 'f45f80da0a2d005530b70f6f6b00756dbf875947a21e533041a05b3c4d629f79',
+ 'electron-v42.2.0-darwin-x64.zip': 'cd6a2d4feca84b7e7b2c4a5a13a443fc3c1173e77b05f798deaab2f0f41002a1',
+ 'electron-v42.2.0-linux-arm64.zip': '1f2037dbdcb8b1327b855ec15fbe3fb8a7f27786b331d17866e88377a0606ad8',
+ 'electron-v42.2.0-linux-x64.zip': '9caeeb15dada37cb3a2d80bf0f5899d175db026a4def11560890bd2f19684909',
+ 'electron-v42.2.0-win32-x64.zip': '6e034b748ad5ed9445bd3da4b7d0792ed49556774a541217b507c156b00dd69a',
+ 'hunspell_dictionaries.zip': '72ea4fdeabf3a49dd86bdeb136c916f7b54952b439edd527321963a5dbb0460d',
}
diff --git a/shared/ios/Podfile.lock b/shared/ios/Podfile.lock
index ac982189cd1b..181d67d909b9 100644
--- a/shared/ios/Podfile.lock
+++ b/shared/ios/Podfile.lock
@@ -27,50 +27,50 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- - ExpoAsset (56.0.11):
+ - ExpoAsset (56.0.13):
- ExpoModulesCore
- - ExpoAudio (56.0.7):
+ - ExpoAudio (56.0.9):
- ExpoModulesCore
- - ExpoCamera (56.0.5):
+ - ExpoCamera (56.0.7):
- ExpoModulesCore
- - ExpoCameraBarcodeScanning (56.0.5):
+ - ExpoCameraBarcodeScanning (56.0.7):
- ExpoCamera
- ZXingObjC/OneD
- ZXingObjC/PDF417
- ExpoClipboard (56.0.3):
- ExpoModulesCore
- - ExpoContacts (56.0.5):
+ - ExpoContacts (56.0.7):
- ExpoModulesCore
- - ExpoDocumentPicker (56.0.3):
+ - ExpoDocumentPicker (56.0.4):
- ExpoModulesCore
- ExpoDomWebView (56.0.5):
- ExpoModulesCore
- - ExpoFileSystem (56.0.6):
+ - ExpoFileSystem (56.0.7):
- ExpoModulesCore
- ExpoFont (56.0.5):
- ExpoModulesCore
- ExpoHaptics (56.0.3):
- ExpoModulesCore
- - ExpoImage (56.0.5):
+ - ExpoImage (56.0.8):
- ExpoModulesCore
- libavif/libdav1d
- SDWebImage (~> 5.21.0)
- SDWebImageAVIFCoder (~> 0.11.0)
- SDWebImageSVGCoder (~> 1.7.0)
- SDWebImageWebPCoder (~> 0.14.6)
- - ExpoImagePicker (56.0.10):
+ - ExpoImagePicker (56.0.12):
- ExpoModulesCore
- ExpoKeepAwake (56.0.3):
- ExpoModulesCore
- - ExpoLocalization (56.0.5):
+ - ExpoLocalization (56.0.6):
- ExpoModulesCore
- - ExpoLocation (56.0.10):
+ - ExpoLocation (56.0.12):
- ExpoModulesCore
- ExpoLogBox (56.0.11):
- React-Core
- - ExpoMailComposer (56.0.3):
+ - ExpoMailComposer (56.0.4):
- ExpoModulesCore
- - ExpoMediaLibrary (56.0.4):
+ - ExpoMediaLibrary (56.0.6):
- ExpoModulesCore
- React-Core
- ExpoModulesCore (56.0.10):
@@ -112,10 +112,10 @@ PODS:
- ExpoModulesCore
- ExpoSMS (56.0.3):
- ExpoModulesCore
- - ExpoTaskManager (56.0.10):
+ - ExpoTaskManager (56.0.13):
- ExpoModulesCore
- UMAppLoader
- - ExpoVideo (56.1.1):
+ - ExpoVideo (56.1.2):
- ExpoModulesCore
- FBLazyVector (0.85.3)
- hermes-engine (250829098.0.10):
@@ -1607,7 +1607,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- - react-native-keyboard-controller (1.21.7):
+ - react-native-keyboard-controller (1.21.8):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -1619,7 +1619,7 @@ PODS:
- React-graphics
- React-ImageManager
- React-jsi
- - react-native-keyboard-controller/common (= 1.21.7)
+ - react-native-keyboard-controller/common (= 1.21.8)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
@@ -1630,7 +1630,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- - react-native-keyboard-controller/common (1.21.7):
+ - react-native-keyboard-controller/common (1.21.8):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -2313,7 +2313,7 @@ PODS:
- ReactNativeDependencies
- RNWorklets
- Yoga
- - RNScreens (4.25.1):
+ - RNScreens (4.25.2):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -2335,9 +2335,9 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- - RNScreens/common (= 4.25.1)
+ - RNScreens/common (= 4.25.2)
- Yoga
- - RNScreens/common (4.25.1):
+ - RNScreens/common (4.25.2):
- hermes-engine
- RCTRequired
- RCTTypeSafety
@@ -2828,33 +2828,33 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
EXConstants: ba815e5adce24a9e1d4ab6775e71cbdacec1896b
Expo: 4710b638d018109e241005907c3a3eaf010657ec
- ExpoAsset: 0e16bd47ecbed3e6f4413f18c56be79fbe29b120
- ExpoAudio: 3a08e84dfc9f516a8c083bcc154df82d2a3c9709
- ExpoCamera: 4d44aace75d08eeb91838a7d08345cd57b2c65f5
- ExpoCameraBarcodeScanning: 25b7b6ce09d5deb09aad0b60cba2fdc1dbccc3e9
+ ExpoAsset: 873ff4f99d9855e222b832c8acedb6b16e83cb2c
+ ExpoAudio: 80f5e1e70e74ba19c2be2ed7c672bcbc12ca1216
+ ExpoCamera: fc1d2688e9824984e28cf606062da40d2d80b13e
+ ExpoCameraBarcodeScanning: a6a8781886a0cf5ebe78184dcc895c0c88e1b8b5
ExpoClipboard: eee843698341fd26d571fd2807800f98abb6123c
- ExpoContacts: 8c7656993c93f7832c5dbbe1fa0a7b62eb3b469c
- ExpoDocumentPicker: 89b97c8810a16cdc89e7596043be4703b44a3664
+ ExpoContacts: cb6d1245197b0e59d12172b662ba461b558e8e45
+ ExpoDocumentPicker: caa9c7c1071dc31262d86a8fe48f51929d291a43
ExpoDomWebView: 27205be8754f9992913b8a9a08e65019f2207075
- ExpoFileSystem: 8a9155648a83de106cd233e51785fc749ae4bed1
+ ExpoFileSystem: d730a1cb33f2c91dec01561f0d710054fda32ae9
ExpoFont: 6fe496f2be617ed4367f159f215dcc4ba27d37d6
ExpoHaptics: 89364cf3c3ca2cfd54cafed42f3be3169bab6d42
- ExpoImage: 8c9c81340ce59dd03190e29e856f78bbe07d7c98
- ExpoImagePicker: 384186680860b92c36c849f83aba18463595702d
+ ExpoImage: b771058800a860377c1be09d493fe458b842e6d8
+ ExpoImagePicker: 43ec8010b8be784970f1cf5834170745b09ecf6b
ExpoKeepAwake: 359c47a1d9ccc3a3c519bca6e39562cce230c5bb
- ExpoLocalization: 553ada00230eb0b72c26247d74f3fb4bf4e87f1f
- ExpoLocation: 833331b6a5de83c1945dbdfaeec8b8aaf19310c8
+ ExpoLocalization: cf50efc436b2ec313510131c1ee0e778366d64b0
+ ExpoLocation: ffb1a2eee4b8d88f0070ba315cb0d57f0c32c7f5
ExpoLogBox: ecf797402cec66c81ca37410f76673d3b86cd32b
- ExpoMailComposer: e202b670f62063851ed74c964034037312d523a8
- ExpoMediaLibrary: 2ee3defd62fa8da9cb80fdcca71968cdeec7b24f
+ ExpoMailComposer: ea650b89d3b93c7a277b11a8ab7010a7be6d595e
+ ExpoMediaLibrary: 5ab82b8ace3f9ab7ac4ca3b2060bd915a4f1568f
ExpoModulesCore: 673b75d1fc0280a28f7dbc14ce85cde0ca52f713
ExpoModulesJSI: bfa734e4199cacb4c81c2800f10ae106d8522e1d
ExpoModulesWorklets: 3cb8b517ea1012e3f0894edb4c12e60f82f24814
ExpoModulesWorkletsAdapter: 39eed1b64e579b995ddaeefb95dab8bbf1f6735b
ExpoScreenCapture: c388565b6244aec9d96c18941efbee8655b2fec2
ExpoSMS: 2cebfd889706da39a397d20c8a68abb0ce7f185d
- ExpoTaskManager: b80b17a7ba85679af8790faa3c25261957a1e196
- ExpoVideo: 0ec19128dc4e224b9debaa5261fc51b22a2cd9c3
+ ExpoTaskManager: 23971cc0a92b61ce06c4dfa521c10627013da497
+ ExpoVideo: 8ebbbf65778c5f986d65fc17bb60b79e9d7624e6
FBLazyVector: 24e62c765683b8d89006a88a2c8f5cf019f0074d
hermes-engine: 615bb48c9c5775981afab071d9298a804bd98a87
KBCommon: bd1f35bb07924f4cc57417b00dab6734747b6423
@@ -2902,7 +2902,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: 3a42e75434099fd10030e36fe9f3b27e3811afd5
React-mutationobservernativemodule: a7f3dc483d3e0e0fade0ea00e2cc95e2e462e023
react-native-kb: 71289be80d93678d5abff5805cb638cd36d9e08e
- react-native-keyboard-controller: 0aad0cfd38b44c097484fe1dc9938bfceb05330c
+ react-native-keyboard-controller: 6d7a5ed594b6370bd9c069ebcd2e7df574b7f995
react-native-netinfo: a05f9b897e76ad24b53f615fed1cbb731932363d
react-native-safe-area-context: 0bbcdfba5c4a1b3203276f1ed9128e3239fdf199
react-native-webview: 2a4da2e03d3ec2c731c4d1a389cb86aaa4902167
@@ -2945,7 +2945,7 @@ SPEC CHECKSUMS:
RNCPicker: a3921be99ae291e420e3e8940e5b2b53e572c543
RNGestureHandler: a97cc64efbfcb7a53969a38310a189a3d5246c65
RNReanimated: dbe1365727409813696964826b407873304f4d51
- RNScreens: defb9f9bbe62721dc7b675e415ade001bd74a4bd
+ RNScreens: 8d318eb8905fa1d3439a1ce5d567f82ae8245657
RNWorklets: 4931990f73bc8f347586918b2e13f11dfadf3b75
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57
diff --git a/shared/package.json b/shared/package.json
index 51a31f16a4cf..ce7e9aa58f2f 100644
--- a/shared/package.json
+++ b/shared/package.json
@@ -101,24 +101,24 @@
"emoji-datasource-apple": "16.0.0",
"emoji-regex": "10.6.0",
"expo": "56.0.0-preview.11",
- "expo-asset": "56.0.11",
- "expo-audio": "56.0.7",
- "expo-camera": "56.0.5",
+ "expo-asset": "56.0.13",
+ "expo-audio": "56.0.9",
+ "expo-camera": "56.0.7",
"expo-clipboard": "56.0.3",
- "expo-contacts": "56.0.5",
- "expo-document-picker": "56.0.3",
- "expo-file-system": "56.0.6",
+ "expo-contacts": "56.0.7",
+ "expo-document-picker": "56.0.4",
+ "expo-file-system": "56.0.7",
"expo-haptics": "56.0.3",
- "expo-image": "56.0.5",
- "expo-image-picker": "56.0.10",
- "expo-localization": "56.0.5",
- "expo-location": "56.0.10",
- "expo-mail-composer": "56.0.3",
- "expo-media-library": "56.0.4",
+ "expo-image": "56.0.8",
+ "expo-image-picker": "56.0.12",
+ "expo-localization": "56.0.6",
+ "expo-location": "56.0.12",
+ "expo-mail-composer": "56.0.4",
+ "expo-media-library": "56.0.6",
"expo-screen-capture": "56.0.4",
"expo-sms": "56.0.3",
- "expo-task-manager": "56.0.10",
- "expo-video": "56.1.1",
+ "expo-task-manager": "56.0.13",
+ "expo-video": "56.1.2",
"google-libphonenumber": "3.2.44",
"immer": "11.1.8",
"lodash": "4.18.1",
@@ -130,10 +130,10 @@
"react-native": "0.85.3",
"react-native-gesture-handler": "2.31.2",
"react-native-kb": "file:../rnmodules/react-native-kb",
- "react-native-keyboard-controller": "1.21.7",
+ "react-native-keyboard-controller": "1.21.8",
"react-native-reanimated": "4.3.1",
"react-native-safe-area-context": "5.8.0",
- "react-native-screens": "4.25.1",
+ "react-native-screens": "4.25.2",
"react-native-web": "0.21.2",
"react-native-webview": "13.16.1",
"react-native-worklets": "0.8.3",
@@ -161,21 +161,21 @@
"@types/jest": "30.0.0",
"@types/lodash": "4.17.24",
"@types/lodash-es": "4.17.12",
- "@types/react": "19.2.14",
+ "@types/react": "19.2.15",
"@types/react-dom": "19.2.3",
"@types/react-measure": "2.0.12",
"@types/webpack-env": "1.18.8",
- "@typescript/native-preview": "7.0.0-dev.20260519.1",
+ "@typescript/native-preview": "7.0.0-dev.20260522.1",
"@welldone-software/why-did-you-render": "10.0.1",
"babel-jest": "30.4.1",
"babel-loader": "10.1.1",
"babel-plugin-module-resolver": "5.0.3",
"babel-plugin-react-compiler": "1.0.0",
"babel-plugin-react-native-web": "0.21.2",
- "babel-preset-expo": "56.0.9",
+ "babel-preset-expo": "56.0.11",
"cross-env": "10.1.0",
"css-loader": "7.1.4",
- "electron": "42.1.0",
+ "electron": "42.2.0",
"eslint": "10.4.0",
"eslint-plugin-deprecation": "3.0.0",
"eslint-plugin-promise": "7.3.0",
@@ -197,13 +197,13 @@
"terser-webpack-plugin": "5.6.0",
"typescript": "6.0.3",
"typescript-eslint": "8.59.4",
- "webpack": "5.106.2",
+ "webpack": "5.107.1",
"webpack-cli": "7.0.2",
"webpack-dev-server": "5.2.4",
"webpack-merge": "6.0.1"
},
"resolutions": {
- "**/@types/react": "19.2.14"
+ "**/@types/react": "19.2.15"
},
"typecoverage": {
"detail": true,
diff --git a/shared/yarn.lock b/shared/yarn.lock
index 9f986534b921..e98500af87f8 100644
--- a/shared/yarn.lock
+++ b/shared/yarn.lock
@@ -1581,6 +1581,15 @@
debug "^4.3.4"
getenv "^2.0.0"
+"@expo/env@~2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@expo/env/-/env-2.3.0.tgz#dbdecd1b64b07840f9d5922a48a06c66bea5fe7c"
+ integrity sha512-9HnnIbzwTTdbwSjNLXTk0fPm9ZwMJ7c1/31tsni8HZ8Q62KzYCyspahH+V365vg5J6lr001DzNwBxVWSaYCQLg==
+ dependencies:
+ chalk "^4.0.0"
+ debug "^4.3.4"
+ getenv "^2.0.0"
+
"@expo/expo-modules-macros-plugin@~0.0.9":
version "0.0.9"
resolved "https://registry.yarnpkg.com/@expo/expo-modules-macros-plugin/-/expo-modules-macros-plugin-0.0.9.tgz#a657f9c3c2dbf1c91bae7ef47238078995cd1bc4"
@@ -1603,6 +1612,19 @@
resolve-from "^5.0.0"
semver "^7.6.0"
+"@expo/image-utils@^0.10.0":
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.10.0.tgz#0009d05174eb1cb2afa271721c40ad19499741d5"
+ integrity sha512-iV1J+F5KpVqfdYsuot+5b8ZBDH6m/jQN2EzQSoa+qOmHqPNck17AihA4X3sso7ghn7p+AHeOKgftwT64amgmkQ==
+ dependencies:
+ "@expo/require-utils" "^56.1.2"
+ "@expo/spawn-async" "^1.8.0"
+ chalk "^4.0.0"
+ getenv "^2.0.0"
+ jimp-compact "0.16.1"
+ parse-png "^2.1.0"
+ semver "^7.6.0"
+
"@expo/image-utils@^0.9.3":
version "0.9.3"
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.9.3.tgz#7e7e60ee376010840ae2f43084c36512ddff45ad"
@@ -1762,6 +1784,15 @@
"@babel/core" "^7.25.2"
"@babel/plugin-transform-modules-commonjs" "^7.24.8"
+"@expo/require-utils@^56.1.2":
+ version "56.1.2"
+ resolved "https://registry.yarnpkg.com/@expo/require-utils/-/require-utils-56.1.2.tgz#7570644445ed415a4e3f39ae71fa17b6012c5aed"
+ integrity sha512-j+zlUQK7xPTKlR9honSLN4umd4czOpNBPibJhOQVxSfT3IP8UJR+7aFvccj5dbuYiBCzDy8vxuDm3AGa0onR8Q==
+ dependencies:
+ "@babel/code-frame" "^7.20.0"
+ "@babel/core" "^7.25.2"
+ "@babel/plugin-transform-modules-commonjs" "^7.24.8"
+
"@expo/router-server@^56.0.9":
version "56.0.9"
resolved "https://registry.yarnpkg.com/@expo/router-server/-/router-server-56.0.9.tgz#3f1f09041798b4aebef0e9ba76c5228c52aab264"
@@ -1786,6 +1817,13 @@
dependencies:
cross-spawn "^7.0.3"
+"@expo/spawn-async@^1.8.0":
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.8.0.tgz#915e2ab38554fca7d788e16032078554dee633cf"
+ integrity sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==
+ dependencies:
+ cross-spawn "^7.0.6"
+
"@expo/sudo-prompt@^9.3.1":
version "9.3.2"
resolved "https://registry.yarnpkg.com/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz#0fd2813402a42988e49145cab220e25bea74b308"
@@ -3181,28 +3219,12 @@
resolved "https://registry.yarnpkg.com/@types/emscripten/-/emscripten-1.41.5.tgz#5670e4b52b098691cb844b84ee48c9176699b68d"
integrity sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==
-"@types/eslint-scope@^3.7.7":
- version "3.7.7"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
- integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint@*":
- version "9.6.1"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584"
- integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
"@types/esrecurse@^4.3.1":
version "4.3.1"
resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec"
integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==
-"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6", "@types/estree@^1.0.8":
+"@types/estree@^1.0.0", "@types/estree@^1.0.6", "@types/estree@^1.0.8":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24"
integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==
@@ -3309,7 +3331,7 @@
"@types/tough-cookie" "*"
parse5 "^7.0.0"
-"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+"@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
@@ -3386,10 +3408,10 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@19.2.14":
- version "19.2.14"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad"
- integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==
+"@types/react@*", "@types/react@19.2.15":
+ version "19.2.15"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.15.tgz#9e2c6a4251a290f5c525c3143caa872fa6e01e0d"
+ integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==
dependencies:
csstype "^3.2.2"
@@ -3626,53 +3648,53 @@
"@typescript-eslint/types" "8.59.4"
eslint-visitor-keys "^5.0.0"
-"@typescript/native-preview-darwin-arm64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260519.1.tgz#deaa14299366a79917a6ec19fc7f240304c36afe"
- integrity sha512-c9zdG6sGJf25Jpz04JgE23zhYeprqFypDGuqiX94yMTvR8IWXjq3R2oMnim66YLBDon/V1nCEy6cFixeSd/4fg==
-
-"@typescript/native-preview-darwin-x64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260519.1.tgz#a6348c134204afdcdfa8f9a0925091788fc994d7"
- integrity sha512-N16V3wiM0tsNmSSA7nZrxqXXt5OCJxBwiCVn35rnA7fr4WzJw6rJmwf9heNNhZ6Gh4ne3+Pexajf5akzuHR75Q==
-
-"@typescript/native-preview-linux-arm64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260519.1.tgz#04f2f0b314e646a6921cc818db6bac92364bab24"
- integrity sha512-ltf91vAwKdbu0SlRQbFgi1h5ZrLLrBn6a4qIeN2VILGbtYrCXnARHRznLBv81yUETQ7aVr/LSQcmsWo1ejCK0w==
-
-"@typescript/native-preview-linux-arm@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260519.1.tgz#ad10403fb8ae6e2ed120cb9f81a029328a00e995"
- integrity sha512-8v4BExeeuCTrhaSGfeIJqm3qQkTzlZix/Qd/FkPlWoz9f7d7COvXb3Z4qhbaVolL0MMnUvQ7m005Z4kYsZ645A==
-
-"@typescript/native-preview-linux-x64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260519.1.tgz#dde79208611da3855f995e595b9b790c622f726f"
- integrity sha512-AVD0tczTtFCHNa4RQRVPvu8Hnw4P3hQ+OlUAjnz/lHowvc6o1pYB46elMqfDuaoWqIpv+EAkAPP4ipFCofJ5IA==
-
-"@typescript/native-preview-win32-arm64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260519.1.tgz#74d0cf98c75e2df20772ae2fa4c8139066e2cf7f"
- integrity sha512-TM+qatljyejqjHevCta3WIH53i0oGC7K8SoJ6t+mf4cGMTpZTyd7NhC1ts7e6/aydZnG53Bsta2iQi1SMIlQEw==
-
-"@typescript/native-preview-win32-x64@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260519.1.tgz#a8cfe9f730399cdc21a20c40a59aee15eef75a18"
- integrity sha512-r9LEsoY7JC/82gXo8hlOmpQaUXcqmngCVOv+mUx1UeMt9f+1S6oNO0W48o75mlBqqC7jfcMHqw8YS4LfVxPRGw==
-
-"@typescript/native-preview@7.0.0-dev.20260519.1":
- version "7.0.0-dev.20260519.1"
- resolved "https://registry.yarnpkg.com/@typescript/native-preview/-/native-preview-7.0.0-dev.20260519.1.tgz#0d865f5cc6d376d896fc031dfbb38ed0004153c6"
- integrity sha512-VVER7vFUDdfm5k3jbH5765tVEJa7+0rTUkFeXyGYrXPxpw9BIjA0QDxdtdlRyaU8MCZV9IKZUo6doxeAQRAjPg==
+"@typescript/native-preview-darwin-arm64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260522.1.tgz#0159da826e3aca345c18f09136a5af932d34c7b8"
+ integrity sha512-kp2JX8wSCQ5i8f5Zr30t9TdAIEw8s3MCvT1zEe6PJAHU7aUHTnRCM+oAHEM8QxM29VD/PxR6b5dVKxHtX7Ny/A==
+
+"@typescript/native-preview-darwin-x64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260522.1.tgz#e39fc3fa2177ad23530da5c648ec249e2407013b"
+ integrity sha512-jX89E/hV/r0fOcP4+JWdKSugJXmWbOO74aC5a1U8+U7TBD1TTfK50Da+BL8Sf2Xud2ybqQ+2FUHAlBsLTC743g==
+
+"@typescript/native-preview-linux-arm64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260522.1.tgz#bce0733d1667d3f48b383679c40f9b9898306f2a"
+ integrity sha512-XFLpJNscLKqT6LLSBrv78DzLdzwFYOePeluRBF616KUPEcfiXliTan1YuHQh4m9i9o6tvW/NnuAPlr/Q8DObsg==
+
+"@typescript/native-preview-linux-arm@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260522.1.tgz#030ff78d23760ae95eaf4aca5323dd385c2ab56b"
+ integrity sha512-vItWfPtZfUQLaVJe0iClfsKSDtkUCUTp2LfEXBHqBCdWhoE3irg7Ivs41l3sp4zXlTio5SUvMzvO8/Mc2SImzA==
+
+"@typescript/native-preview-linux-x64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260522.1.tgz#b8d7f6ad07007e55029989e474a0950cd1154579"
+ integrity sha512-r5wkbqL+qpI3QN3GiF0dg5A0PD5x9cfYSSCD744+Ov35P8inuJSySjR44gdoOF3IcFTupPEc/4q4to/gId1kZQ==
+
+"@typescript/native-preview-win32-arm64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260522.1.tgz#8245d5270c2f4637ee4b6e6abfb5b4f0f890b458"
+ integrity sha512-Xtqw0dfE35WISVqN2J9hp21N6l5MT21DSgJ4w/cXisDZlaeT0Vq57CgUWUbLT4CEw5gs0QK6kmUFOSAP7Dga6Q==
+
+"@typescript/native-preview-win32-x64@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260522.1.tgz#59e9319ef2294371b8f162835bc48d96aa49f4ca"
+ integrity sha512-CBTpf7skuTNgHzjwEbw3ujU76r/u9asENXc9xQmgj6qCJ1+ZHn34MMzolkUegwtHWfuem38ayYTJOMn2dmNQ5g==
+
+"@typescript/native-preview@7.0.0-dev.20260522.1":
+ version "7.0.0-dev.20260522.1"
+ resolved "https://registry.yarnpkg.com/@typescript/native-preview/-/native-preview-7.0.0-dev.20260522.1.tgz#ab9e14b732e50b71c29df6402ed722236fa105ed"
+ integrity sha512-NgnE3PE3/nr4Qr8p1uCIkaM5YmNa8hQGhzb8Olb6w09aocg4Wq8bjrFHe4qZH2kgV+Hl4gGXRNb9ww1P2fwc9Q==
optionalDependencies:
- "@typescript/native-preview-darwin-arm64" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-darwin-x64" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-linux-arm" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-linux-arm64" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-linux-x64" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-win32-arm64" "7.0.0-dev.20260519.1"
- "@typescript/native-preview-win32-x64" "7.0.0-dev.20260519.1"
+ "@typescript/native-preview-darwin-arm64" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-darwin-x64" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-linux-arm" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-linux-arm64" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-linux-x64" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-win32-arm64" "7.0.0-dev.20260522.1"
+ "@typescript/native-preview-win32-x64" "7.0.0-dev.20260522.1"
"@ungap/structured-clone@^1.3.0":
version "1.3.1"
@@ -4397,7 +4419,55 @@ babel-preset-current-node-syntax@^1.2.0:
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
"@babel/plugin-syntax-top-level-await" "^7.14.5"
-babel-preset-expo@56.0.9, babel-preset-expo@~56.0.8:
+babel-preset-expo@56.0.11:
+ version "56.0.11"
+ resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-56.0.11.tgz#34004e0cbf87549f53e92786fddf09109bbcf511"
+ integrity sha512-cNOBuiRw02SYtHrZUmJNyOxxnIaKpfUrD3bwz4xaftLOc2whBX5nSwbk46og7jr97/VcV7dDtO7g5lf3y93Jrw==
+ dependencies:
+ "@babel/generator" "^7.20.5"
+ "@babel/helper-module-imports" "^7.25.9"
+ "@babel/plugin-proposal-decorators" "^7.12.9"
+ "@babel/plugin-proposal-export-default-from" "^7.24.7"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+ "@babel/plugin-syntax-export-default-from" "^7.24.7"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-transform-async-generator-functions" "^7.25.4"
+ "@babel/plugin-transform-async-to-generator" "^7.24.7"
+ "@babel/plugin-transform-block-scoping" "^7.25.0"
+ "@babel/plugin-transform-class-properties" "^7.25.4"
+ "@babel/plugin-transform-class-static-block" "^7.27.1"
+ "@babel/plugin-transform-classes" "^7.25.4"
+ "@babel/plugin-transform-destructuring" "^7.24.8"
+ "@babel/plugin-transform-export-namespace-from" "^7.25.9"
+ "@babel/plugin-transform-flow-strip-types" "^7.25.2"
+ "@babel/plugin-transform-for-of" "^7.24.7"
+ "@babel/plugin-transform-logical-assignment-operators" "^7.24.7"
+ "@babel/plugin-transform-modules-commonjs" "^7.24.8"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.24.7"
+ "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.7"
+ "@babel/plugin-transform-object-rest-spread" "^7.24.7"
+ "@babel/plugin-transform-optional-catch-binding" "^7.24.7"
+ "@babel/plugin-transform-optional-chaining" "^7.24.8"
+ "@babel/plugin-transform-parameters" "^7.24.7"
+ "@babel/plugin-transform-private-methods" "^7.24.7"
+ "@babel/plugin-transform-private-property-in-object" "^7.24.7"
+ "@babel/plugin-transform-react-display-name" "^7.24.7"
+ "@babel/plugin-transform-react-jsx" "^7.28.6"
+ "@babel/plugin-transform-react-jsx-development" "^7.27.1"
+ "@babel/plugin-transform-react-pure-annotations" "^7.27.1"
+ "@babel/plugin-transform-runtime" "^7.24.7"
+ "@babel/plugin-transform-typescript" "^7.25.2"
+ "@babel/plugin-transform-unicode-regex" "^7.24.7"
+ "@babel/preset-typescript" "^7.23.0"
+ "@react-native/babel-plugin-codegen" "0.85.3"
+ babel-plugin-react-compiler "^1.0.0"
+ babel-plugin-react-native-web "~0.21.0"
+ babel-plugin-syntax-hermes-parser "^0.33.3"
+ babel-plugin-transform-flow-enums "^0.0.2"
+ debug "^4.3.4"
+
+babel-preset-expo@~56.0.8:
version "56.0.9"
resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-56.0.9.tgz#148e90f2259861943b6e2e44e8b45c60e3913976"
integrity sha512-pDQ4zrxvWSzUZzYBbV/fnhZjjxxJxorF51kHuFDX2u4U/WEBAeuZTf2oOI5A+6RgGf81eiH1mXMwEY53C+a3jw==
@@ -5401,10 +5471,10 @@ electron-to-chromium@^1.5.328:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.359.tgz#51d3f2dd176a357b3390759b552c74cd4b6abe8e"
integrity sha512-8lPELWuYZIWk7NDvCNthtmMw/7Q5Wu25NpM4djFMHBmk8DubPAtL4YTOp7ou0e7HyJtwkVlWv8XMLURnrtgJQw==
-electron@42.1.0:
- version "42.1.0"
- resolved "https://registry.yarnpkg.com/electron/-/electron-42.1.0.tgz#e0ba4ee476c5620e0f0f6eed6fb039eafe6b7625"
- integrity sha512-0szNwC/0dWtkvNce5j3ThiuL0TxBNrZN/BZhdOiGwbLreiD/+u3MGpkct4hA5Ycagb8MXjpEr5/oosi+FwuKRQ==
+electron@42.2.0:
+ version "42.2.0"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-42.2.0.tgz#513ac2d86c2a3217c17d25f24809bc46d470e7c2"
+ integrity sha512-b2Tc7sIKiZEl0tBVwFM5GJ+FT5KYhmy9QJHjx8BGVZPVW2SctXWEvrE959ElB56qw7H05dBkhlikDA1DmpaAMw==
dependencies:
"@electron/get" "^5.0.0"
"@types/node" "^24.9.0"
@@ -5457,10 +5527,10 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
-enhanced-resolve@^5.20.0:
- version "5.21.5"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz#8f80167d009d8f01267ad61035e59fe5c94ac3a6"
- integrity sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==
+enhanced-resolve@^5.21.4:
+ version "5.22.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz#43c5caad657c6fce58fc6142e5ca6fa8528ed460"
+ integrity sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.3.3"
@@ -5609,7 +5679,7 @@ es-iterator-helpers@^1.2.1:
iterator.prototype "^1.1.5"
math-intrinsics "^1.1.0"
-es-module-lexer@^2.0.0:
+es-module-lexer@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.1.0.tgz#1dfcbb5ea3bbfb63f28e1fc3676c3676d1c9624c"
integrity sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==
@@ -5988,7 +6058,15 @@ expect@30.4.1, expect@^30.0.0:
jest-mock "30.4.1"
jest-util "30.4.1"
-expo-asset@56.0.11, expo-asset@~56.0.9:
+expo-asset@56.0.13:
+ version "56.0.13"
+ resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-56.0.13.tgz#8a034941cd6bc8a6a86d0d0e8a11d36e20c0833e"
+ integrity sha512-lNCZzRa9sk842/xULSZis0yT396DNTHIgkLDcuwjaHZgSLYEkHF32azyHlWrbCUaCu62yRaFSCXSQYKMaL/vYQ==
+ dependencies:
+ "@expo/image-utils" "^0.10.0"
+ expo-constants "~56.0.14"
+
+expo-asset@~56.0.9:
version "56.0.11"
resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-56.0.11.tgz#bcd828a11a399a3872656d9aafa2f3f50226d5e5"
integrity sha512-4FZsqF8lLM5Q8/vHom96iU0EDocdaVBalo4U0L8mF7LV2mt0DUsSNWAuUnxK2d92+srGUckMwcim6z+LNCYjrw==
@@ -5996,15 +6074,15 @@ expo-asset@56.0.11, expo-asset@~56.0.9:
"@expo/image-utils" "^0.9.3"
expo-constants "~56.0.12"
-expo-audio@56.0.7:
- version "56.0.7"
- resolved "https://registry.yarnpkg.com/expo-audio/-/expo-audio-56.0.7.tgz#99fd54044d7363828b889765e9969288a7b89895"
- integrity sha512-8KIayOmbS3ilNZJP5ky+X6iKjaWq8ARfpBML6lu1eVCXLhlcZXax+iDkfxMNIekZ3RiuwTold7b7eCZiQMEhaQ==
+expo-audio@56.0.9:
+ version "56.0.9"
+ resolved "https://registry.yarnpkg.com/expo-audio/-/expo-audio-56.0.9.tgz#746db0b48179613a1ab3f99feb4472b7265e0794"
+ integrity sha512-WBvHoftoCru/PXyHDt+D5y/wOEeKZc/F6MY3X6WbLWlOHW5wfo6h9DxhS3BfxmjZ2Q3KJxCQWIIB8ASIFAJpUQ==
-expo-camera@56.0.5:
- version "56.0.5"
- resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-56.0.5.tgz#b230b288bcca48c2d7b2f4538c4c2301d301138f"
- integrity sha512-mJXk4a/f07XT7TXQZ49KI4gk5+kARzJDAghVkZ3q+Fjyb/mEJ4fsYWz3yVfx3sb1pAaoq0AX5821yd8x2vo+cA==
+expo-camera@56.0.7:
+ version "56.0.7"
+ resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-56.0.7.tgz#47603bf799d223adecfae16726085dec1901b47a"
+ integrity sha512-c8z+UheidFintQyP9XLEDP43aK4PS/o9+TFLW0zEOjdqkYCBgoWq6Mw/Ps62kjBeftFY7xrp5ZLITbenNvbTaw==
dependencies:
barcode-detector "^3.0.0"
@@ -6020,17 +6098,29 @@ expo-constants@~56.0.10, expo-constants@~56.0.12:
dependencies:
"@expo/env" "~2.2.0"
-expo-contacts@56.0.5:
- version "56.0.5"
- resolved "https://registry.yarnpkg.com/expo-contacts/-/expo-contacts-56.0.5.tgz#1fe8c6b0ee92cfe982183e5d74f274240b1adcf1"
- integrity sha512-AB17WiCYk1B9ybSK1d2T5riuBMNRWk1MzOA71MHG8r0L7evhL1RMP2AssfWCgJa4BWarwCwd0FFNArjDYribIg==
+expo-constants@~56.0.14:
+ version "56.0.14"
+ resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-56.0.14.tgz#8f44977fae74dd0bc3834fe149eccf2d77aefe39"
+ integrity sha512-NeFIFXi+RAB5ayR/CPiQXRab0HczkA+BQfF8uci4G3RMBSy+uzd+1skRx/uqLUo3OYjSfs6LUQ8JDVbRgJRRQQ==
+ dependencies:
+ "@expo/env" "~2.3.0"
-expo-document-picker@56.0.3:
- version "56.0.3"
- resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-56.0.3.tgz#0a217a74f26f9d7291f55cf46a8c878f120e28bc"
- integrity sha512-6ibeQkWfLl3xM9sNUaYthdqkz4d5UMIutDZhwgPDAZQZVxOFqE++u1zT1NO4JdmuUuOiOgsbZ5oYVQ1YBiKBtQ==
+expo-contacts@56.0.7:
+ version "56.0.7"
+ resolved "https://registry.yarnpkg.com/expo-contacts/-/expo-contacts-56.0.7.tgz#ec014bd63302906bb56de0171947d06d43e5182f"
+ integrity sha512-2WQv1i2b3UIH3rnIfJ1RjyifUiWi9fSXM5Eb5xPBxaPK1GpTjQeo4ozNgPRzUV55VZ9/Q2peU1EyFe3obVULaw==
+
+expo-document-picker@56.0.4:
+ version "56.0.4"
+ resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-56.0.4.tgz#ee80925f6dc212622fd6fd9846784774ea4b1d15"
+ integrity sha512-75Apf74XNkYYohObIH19VZw42xpe0gmEnPccuzGXKVAzlvTYCfibSgW17F+6vt4paOfZEnAoZ1QFZM6dmaujRA==
+
+expo-file-system@56.0.7:
+ version "56.0.7"
+ resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-56.0.7.tgz#66a69361d839afa1773dc247b160da166e3df0da"
+ integrity sha512-dcKzo8ShPloM7jgfnMcJStgQebhP8owVjCkNI/aX6NMFV1CYB8bxKGMdnzJ3mXk5nfaiW+F/lSKr2UIJ02WAUA==
-expo-file-system@56.0.6, expo-file-system@~56.0.4:
+expo-file-system@~56.0.4:
version "56.0.6"
resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-56.0.6.tgz#5d1002f0dea400502ba94a05bee88ef60a5537db"
integrity sha512-GWuCg5SvSCOHz7254mqF9zKO9O3uSDCc9KUiRHH1b6DL/V6QOjN2ijtRQIHUP0EEXxGtFAxBE0qtpPGMQmYkbg==
@@ -6052,17 +6142,17 @@ expo-image-loader@~56.0.3:
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-56.0.3.tgz#665112665e78d0fd9629f6160e2ca419d7f0c5c6"
integrity sha512-JgUo4fUeU1ZC+z8iBFj8v7yoGQnZrLbOVPyNE+DWVrld55F2F6R1ck+rmdm/8TNWLz1LhNQfD7c3XYP1ZikxXA==
-expo-image-picker@56.0.10:
- version "56.0.10"
- resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-56.0.10.tgz#602dfae453316ae2c45e49085aebdc5a45a15d67"
- integrity sha512-WFIDWkl9UtGiv4SXDeNv1uwqJZlrmUm9xB7cqqRiwXtGrYXa6SFDYvugRmwFJjhKEUDbvinAHYV6fYqjPPVu3A==
+expo-image-picker@56.0.12:
+ version "56.0.12"
+ resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-56.0.12.tgz#a078e9f6bdd1b760b3dc7fe987484f3cf0aa7d49"
+ integrity sha512-ogXY0PVjpwYzwaqcWl6Y3IypdWRsPJ7iNtgnXLdJ9nU7+uryy0nzfMfdMdo9QZFLy/CH5NZEkD8BM+FeRwsUbg==
dependencies:
expo-image-loader "~56.0.3"
-expo-image@56.0.5:
- version "56.0.5"
- resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-56.0.5.tgz#662ad7e1662d17d6e7db001e176a6aa0fb7aac5c"
- integrity sha512-hUpm0ek7iRsC/WXxP/8AdM6bSxmeNPqTNXEZlcNrkirEtaNYrjs7t/QLjsNTME+q5Oo4cXV2R5iALQMiLL6rhA==
+expo-image@56.0.8:
+ version "56.0.8"
+ resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-56.0.8.tgz#b3d8b19fb130fceef7c762a358b17f23c87a9515"
+ integrity sha512-sychCmoLwzvawXRpl46scL9xL7Kj2ft+aqiCvYI+GvKq3bzHJPvuith1Dv6dzRFD34/wyvBn18lY31KUY1sRmg==
dependencies:
sf-symbols-typescript "^2.2.0"
@@ -6071,29 +6161,29 @@ expo-keep-awake@~56.0.3:
resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-56.0.3.tgz#88991b4859c77af73cf68c500839a18fe5d8febe"
integrity sha512-CLMJXtEiMKknD3Rpm8CRwE6ZJUzu2yCEmRk1sgfHAJ1zIbuEWY3dpPDubtsnuzWm+2k6Sru+yaFbYsvPWmTiBA==
-expo-localization@56.0.5:
- version "56.0.5"
- resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-56.0.5.tgz#d5a8d9e036bc2accb24269a9a6477af8b3ae2fb3"
- integrity sha512-/ZpQQZPa7r1MRlv3of5weLCJuyfbUxsL5dW1Y+KgoUj78CrBMGOENZbyQrg/lBWr0GhTMAgzYZTVjiyy470STQ==
+expo-localization@56.0.6:
+ version "56.0.6"
+ resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-56.0.6.tgz#386663c5cb137d763e055feabeb74ec2b43b2194"
+ integrity sha512-zzBVoUFHCVNBywcxGsspoZeIXebihOo/AnmQYE4jMv8gHCSKlLNFT+ft+0+mWcZCMs9necvUs8S8TDonAu/xBA==
dependencies:
rtl-detect "^1.0.2"
-expo-location@56.0.10:
- version "56.0.10"
- resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-56.0.10.tgz#c5e20faf44dcd62b27d9031e559c27fe4720a4fd"
- integrity sha512-YuQ9bHEngWYnuHNb9NTQU/cxObgcUbe6pe3b1+5+v32E/H9v6uwJ/TRFwpF+NC7LejLq61W0u8SdT/+b/ERxJQ==
+expo-location@56.0.12:
+ version "56.0.12"
+ resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-56.0.12.tgz#241915a5d006b43e94f4e1a8e416bc29d13a43b7"
+ integrity sha512-v5zKm1RML4SylM1RYu7LxKt0n4vC4eFCRIQCRzemiSPoUAL/ktuW+kUYuwFFqCKRJ8ZsBzxpSQpdVn+tzeeXkg==
dependencies:
- "@expo/image-utils" "^0.9.3"
+ "@expo/image-utils" "^0.10.0"
-expo-mail-composer@56.0.3:
- version "56.0.3"
- resolved "https://registry.yarnpkg.com/expo-mail-composer/-/expo-mail-composer-56.0.3.tgz#a603d99e96a11ccb2df332e241befddf2541e143"
- integrity sha512-y7GRlH1+501K3tzqVne5+nuD8LIqoEy+0tnZqb4rS6eCG7bl9RTR1fAE9OsL+72dA3v0uZN7ueQCj7nSERWhMQ==
-
-expo-media-library@56.0.4:
+expo-mail-composer@56.0.4:
version "56.0.4"
- resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-56.0.4.tgz#513360bfc40dba4aeb932a59e309d291145eb0d8"
- integrity sha512-ySRhsYUjQUEKEfFm9euN2H0KvTfgd+tXoDxWBL9jNdAkEYrFFF+CBZ4MeSNLO/k82ZjBrN1+DCmGn9GP7kkjmA==
+ resolved "https://registry.yarnpkg.com/expo-mail-composer/-/expo-mail-composer-56.0.4.tgz#cb422a4ea49cd61c28251cd4728177d3de6a8192"
+ integrity sha512-8DWyBcKLIFtuPFZKdZeErDoRAfNCl/UJP7uuReY06Bl98YIHEd3DpNyzW5+vMLAxq8Lj8DhDjFBL2U1nDF151g==
+
+expo-media-library@56.0.6:
+ version "56.0.6"
+ resolved "https://registry.yarnpkg.com/expo-media-library/-/expo-media-library-56.0.6.tgz#6fc30bbbe01698bb3e03344b90fd3bcd8cbfb7c5"
+ integrity sha512-UsyVcxP7Op9ErFFLW1xImjoKFgKi7XSw8hrCfzf2yIG+OgVb9dsQth0mVRPgfRxdELagsUslXc1QXTiW8dpbaQ==
expo-modules-autolinking@~56.0.6, expo-modules-autolinking@~56.0.8:
version "56.0.8"
@@ -6134,17 +6224,17 @@ expo-sms@56.0.3:
resolved "https://registry.yarnpkg.com/expo-sms/-/expo-sms-56.0.3.tgz#909e6896124f0ae725f818fbbfcb9d2129cb6f50"
integrity sha512-jfKMFtIDfOQ7d4z9Bjgn8N6zX3hTSYYga9DNLo1I2gR5aUwPxq8SguuPzLckVmPsPRl2v5BmIBOBIeY3mBxlZg==
-expo-task-manager@56.0.10:
- version "56.0.10"
- resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-56.0.10.tgz#9ac77ce0ecd132ebdc3dcc8f7d08bdb7180a692e"
- integrity sha512-9ph5pL65MMWbRg9+psuECCps+zcj3Qm8GDSFCxbvxS4EW7xNUU7SiGywa1DJprIXIA2zOLjfvCxwmcSb1c+oEA==
+expo-task-manager@56.0.13:
+ version "56.0.13"
+ resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-56.0.13.tgz#18786f1c14316d5b78af386ec612906132fd17bd"
+ integrity sha512-5CCd7PXIHf3ocYPmjt0rYPiezNHQ9TpVffYMEXo/HJwgdrn7Xkzmb8TJV0mURsBbe0eexSf4mky0WCnHMcKeZg==
dependencies:
unimodules-app-loader "~56.0.0"
-expo-video@56.1.1:
- version "56.1.1"
- resolved "https://registry.yarnpkg.com/expo-video/-/expo-video-56.1.1.tgz#b7cddbe9542d56b5c739ccfbb2d6e9fc252c4975"
- integrity sha512-oyOUPXnuNzaYk/0FVxYsJ0DMHo54rVSxH7/p9OVF/dyFo7mSaVN7UaH3X8q0kLH2Sfz0cCz2+3wXdsCNk+A+xA==
+expo-video@56.1.2:
+ version "56.1.2"
+ resolved "https://registry.yarnpkg.com/expo-video/-/expo-video-56.1.2.tgz#4260d40186632689b530fd19268de92bb7c11839"
+ integrity sha512-NPdbwcPfPd7lgOcgTFi7m7Dqp1+3OSXJHrUDU4tXA/Ly4vuNpe35eZNA21F/OJtJqk/nw3r6PDZnSqw5XY0p6w==
expo@56.0.0-preview.11:
version "56.0.0-preview.11"
@@ -8194,7 +8284,7 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-loader-runner@^4.3.1:
+loader-runner@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.2.tgz#9913d3a15971f8f635915e601fb5c9d495d918e9"
integrity sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==
@@ -9658,10 +9748,10 @@ react-native-is-edge-to-edge@^1.2.1, react-native-is-edge-to-edge@^1.3.1:
"react-native-kb@file:../rnmodules/react-native-kb":
version "0.1.1"
-react-native-keyboard-controller@1.21.7:
- version "1.21.7"
- resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.21.7.tgz#2e9c6ab2d309a81d678420dcaaa7314b0324d42d"
- integrity sha512-gs+8nI8HYnRdDt4NWbk1iVuS6kDLf2taJvp+h/TjM1FBdtnQmlYLJ6buNiUqSnkIH4OFEAxdNr3/GOOYdLfkUQ==
+react-native-keyboard-controller@1.21.8:
+ version "1.21.8"
+ resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.21.8.tgz#ae21d08fa463d638ece7e099a0af41f412cffed6"
+ integrity sha512-fPpRb1DkG0mgjNdsVvwKTeEqbTdUymZ7ZEqe5kXPEhEjnvo+3BX3p4CBhg3dEzfonL/hGGSS1dBDMDjiQbAkBQ==
dependencies:
react-native-is-edge-to-edge "^1.2.1"
@@ -9678,10 +9768,10 @@ react-native-safe-area-context@5.8.0:
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.8.0.tgz#d2fb9fc8e0f0583311e261dc484b95ceb80f8902"
integrity sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==
-react-native-screens@4.25.1:
- version "4.25.1"
- resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.25.1.tgz#ec00836805fc1d820eae067f583f3056c9aa6f55"
- integrity sha512-9gAFwkzcvBrQHIQjIT+hz1gUows1hqCEkp40oj+Wh3jXo6aG8mysFom9++HDKSLaOk2rgSUTaUgKOTff9c2udQ==
+react-native-screens@4.25.2:
+ version "4.25.2"
+ resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.25.2.tgz#f12c3d8d2cb9d69e1267af1bbba1b37d1d05452d"
+ integrity sha512-1Nj1fusFd+rIMKU/qC9yGKVG+3ofh11d3OdBQKL1iVvQfKvcB8vhvTGQf2TkfxW3bamxN+hCZIXmNuU0mRkyDg==
dependencies:
react-freeze "^1.0.0"
warn-once "^0.1.0"
@@ -10744,7 +10834,7 @@ terminal-link@^2.1.1:
ansi-escapes "^4.2.1"
supports-hyperlinks "^2.0.0"
-terser-webpack-plugin@5.6.0, terser-webpack-plugin@^5.3.17:
+terser-webpack-plugin@5.6.0, terser-webpack-plugin@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz#8e7caad248183ab9e91ff08a83b0fc9f0439c3c3"
integrity sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==
@@ -11303,22 +11393,21 @@ webpack-merge@6.0.1, webpack-merge@^6.0.1:
flat "^5.0.2"
wildcard "^2.0.1"
-webpack-sources@^3.3.4:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.4.1.tgz#009d110999ebd9fb3a6fa8d32eec6f84d940e65d"
- integrity sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==
+webpack-sources@^3.4.1:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.5.0.tgz#87bf7f5801a4e985b1f1c92b64b9620a02f76d08"
+ integrity sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==
webpack-virtual-modules@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
-webpack@5.106.2:
- version "5.106.2"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.106.2.tgz#ca8174b4fd80f055cc5a45fcc5577d6db76c8ac5"
- integrity sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==
+webpack@5.107.1:
+ version "5.107.1"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.107.1.tgz#01ad63131b7c413f607cc00a8136f467c1f10af0"
+ integrity sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==
dependencies:
- "@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.8"
"@types/json-schema" "^7.0.15"
"@webassemblyjs/ast" "^1.14.1"
@@ -11328,20 +11417,20 @@ webpack@5.106.2:
acorn-import-phases "^1.0.3"
browserslist "^4.28.1"
chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.20.0"
- es-module-lexer "^2.0.0"
+ enhanced-resolve "^5.21.4"
+ es-module-lexer "^2.1.0"
eslint-scope "5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.11"
- loader-runner "^4.3.1"
+ loader-runner "^4.3.2"
mime-db "^1.54.0"
neo-async "^2.6.2"
schema-utils "^4.3.3"
tapable "^2.3.0"
- terser-webpack-plugin "^5.3.17"
+ terser-webpack-plugin "^5.5.0"
watchpack "^2.5.1"
- webpack-sources "^3.3.4"
+ webpack-sources "^3.4.1"
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4"